10f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
20f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * mos7720.c
30f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *   Controls the Moschip 7720 usb to dual port serial convertor
40f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *
50f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * Copyright 2006 Moschip Semiconductor Tech. Ltd.
60f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *
70f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * This program is free software; you can redistribute it and/or modify
80f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * it under the terms of the GNU General Public License as published by
90f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * the Free Software Foundation, version 2 of the License.
100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *
110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * Developed by:
1250d2dc7266573dfbdc84fc207494dd21315782efGreg Kroah-Hartman * 	Vijaya Kumar <vijaykumar.gn@gmail.com>
1350d2dc7266573dfbdc84fc207494dd21315782efGreg Kroah-Hartman *	Ajay Kumar <naanuajay@yahoo.com>
1450d2dc7266573dfbdc84fc207494dd21315782efGreg Kroah-Hartman *	Gurudeva <ngurudeva@yahoo.com>
150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *
160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * Cleaned up from the original by:
170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	Greg Kroah-Hartman <gregkh@suse.de>
180f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *
190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * Originally based on drivers/usb/serial/io_edgeport.c which is:
200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	Copyright (C) 2000 Inside Out Networks, All rights reserved.
210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
220f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman */
230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/kernel.h>
240f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/errno.h>
250f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/init.h>
260f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/slab.h>
270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/tty.h>
280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/tty_driver.h>
290f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/tty_flip.h>
300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/module.h>
310f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/spinlock.h>
320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/serial.h>
330f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/serial_reg.h>
340f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/usb.h>
350f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#include <linux/usb/serial.h>
364da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox#include <linux/uaccess.h>
37b69578df7e98659b7d94c905971a6d1025b431adMike Dunn#include <linux/parport.h>
380f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
390f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
400f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * Version Information
410f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman */
4263b917678fe6d63e633462b5be5a309511bcf3caMike Dunn#define DRIVER_VERSION "2.1"
430f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define DRIVER_AUTHOR "Aspire Communications pvt Ltd."
440f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define DRIVER_DESC "Moschip USB Serial Driver"
450f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
460f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/* default urb timeout */
470f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define MOS_WDR_TIMEOUT	(HZ * 5)
480f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
490f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define MOS_MAX_PORT	0x02
500f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define MOS_WRITE	0x0E
510f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define MOS_READ	0x0D
520f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
530f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/* Interrupt Rotinue Defines	*/
540f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define SERIAL_IIR_RLS	0x06
550f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define SERIAL_IIR_RDA	0x04
560f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define SERIAL_IIR_CTI	0x0c
570f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define SERIAL_IIR_THR	0x02
580f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define SERIAL_IIR_MS	0x00
590f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
600f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define NUM_URBS			16	/* URB Count */
610f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define URB_TRANSFER_BUFFER_SIZE	32	/* URB Size */
620f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
63b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/* This structure holds all of the local serial port information */
644da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Coxstruct moschip_port {
650f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u8	shadowLCR;		/* last LCR value received */
660f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u8	shadowMCR;		/* last MCR value received */
670f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u8	shadowMSR;		/* last MSR value received */
680f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	char			open;
690f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct async_icount	icount;
700f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial_port	*port;	/* loop back to the owner */
710f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct urb		*write_urb_pool[NUM_URBS];
720f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman};
730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
7490ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool debug;
750f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
76fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunnstatic struct usb_serial_driver moschip7720_2port_driver;
77fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
780f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define USB_VENDOR_ID_MOSCHIP		0x9710
790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define MOSCHIP_DEVICE_ID_7720		0x7720
800f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define MOSCHIP_DEVICE_ID_7715		0x7715
810f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
827d40d7e85a25e01948bcb4dc3eda1355af318337Németh Mártonstatic const struct usb_device_id moschip_port_id_table[] = {
834da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	{ USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7720) },
84fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	{ USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7715) },
850f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{ } /* terminating entry */
860f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman};
870f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-HartmanMODULE_DEVICE_TABLE(usb, moschip_port_id_table);
880f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
89b69578df7e98659b7d94c905971a6d1025b431adMike Dunn#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
90b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
91b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/* initial values for parport regs */
92b69578df7e98659b7d94c905971a6d1025b431adMike Dunn#define DCR_INIT_VAL       0x0c	/* SLCTIN, nINIT */
93b69578df7e98659b7d94c905971a6d1025b431adMike Dunn#define ECR_INIT_VAL       0x00	/* SPP mode */
94b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
95b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstruct urbtracker {
96b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport  *mos_parport;
97b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct list_head        urblist_entry;
98b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct kref             ref_count;
99b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct urb              *urb;
100b69578df7e98659b7d94c905971a6d1025b431adMike Dunn};
101b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
102b69578df7e98659b7d94c905971a6d1025b431adMike Dunnenum mos7715_pp_modes {
103b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	SPP = 0<<5,
104b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	PS2 = 1<<5,      /* moschip calls this 'NIBBLE' mode */
105b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	PPF = 2<<5,	 /* moschip calls this 'CB-FIFO mode */
106b69578df7e98659b7d94c905971a6d1025b431adMike Dunn};
107b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
108b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstruct mos7715_parport {
109b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct parport          *pp;	       /* back to containing struct */
110b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct kref             ref_count;     /* to instance of this struct */
111b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct list_head        deferred_urbs; /* list deferred async urbs */
112b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct list_head        active_urbs;   /* list async urbs in flight */
113b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spinlock_t              listlock;      /* protects list access */
114b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	bool                    msg_pending;   /* usb sync call pending */
115b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct completion       syncmsg_compl; /* usb sync call completed */
116b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct tasklet_struct   urb_tasklet;   /* for sending deferred urbs */
117b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct usb_serial       *serial;       /* back to containing struct */
118b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	__u8	                shadowECR;     /* parallel port regs... */
119b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	__u8	                shadowDCR;
120b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	atomic_t                shadowDSR;     /* updated in int-in callback */
121b69578df7e98659b7d94c905971a6d1025b431adMike Dunn};
122b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
123b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/* lock guards against dereferencing NULL ptr in parport ops callbacks */
124b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic DEFINE_SPINLOCK(release_lock);
125b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
12663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn#endif	/* CONFIG_USB_SERIAL_MOS7715_PARPORT */
12763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn
12863b917678fe6d63e633462b5be5a309511bcf3caMike Dunnstatic const unsigned int dummy; /* for clarity in register access fns */
12963b917678fe6d63e633462b5be5a309511bcf3caMike Dunn
130b69578df7e98659b7d94c905971a6d1025b431adMike Dunnenum mos_regs {
131b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	THR,	          /* serial port regs */
132b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	RHR,
133b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	IER,
134b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	FCR,
135b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	ISR,
136b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	LCR,
137b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	MCR,
138b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	LSR,
139b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	MSR,
140b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	SPR,
141b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	DLL,
142b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	DLM,
143b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	DPR,              /* parallel port regs */
144b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	DSR,
145b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	DCR,
146b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	ECR,
147b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	SP1_REG,          /* device control regs */
148b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	SP2_REG,          /* serial port 2 (7720 only) */
149b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	PP_REG,
150b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	SP_CONTROL_REG,
151b69578df7e98659b7d94c905971a6d1025b431adMike Dunn};
152b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
153b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/*
154b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * Return the correct value for the Windex field of the setup packet
155b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * for a control endpoint message.  See the 7715 datasheet.
156b69578df7e98659b7d94c905971a6d1025b431adMike Dunn */
157b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic inline __u16 get_reg_index(enum mos_regs reg)
158b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
159b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	static const __u16 mos7715_index_lookup_table[] = {
160b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x00,		/* THR */
161b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x00,		/* RHR */
162b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x01,		/* IER */
163b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x02,		/* FCR */
164b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x02,		/* ISR */
165b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x03,		/* LCR */
166b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x04,		/* MCR */
167b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x05,		/* LSR */
168b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x06,		/* MSR */
169b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x07,		/* SPR */
170b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x00,		/* DLL */
171b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x01,		/* DLM */
172b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x00,		/* DPR */
173b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x01,		/* DSR */
174b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x02,		/* DCR */
175b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x0a,		/* ECR */
176b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x01,		/* SP1_REG */
177b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x02,		/* SP2_REG (7720 only) */
178b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x04,		/* PP_REG (7715 only) */
179b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		0x08,		/* SP_CONTROL_REG */
180b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	};
181b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return mos7715_index_lookup_table[reg];
182b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
183b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
184b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/*
185b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * Return the correct value for the upper byte of the Wvalue field of
186b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * the setup packet for a control endpoint message.
187b69578df7e98659b7d94c905971a6d1025b431adMike Dunn */
18863b917678fe6d63e633462b5be5a309511bcf3caMike Dunnstatic inline __u16 get_reg_value(enum mos_regs reg,
18963b917678fe6d63e633462b5be5a309511bcf3caMike Dunn				  unsigned int serial_portnum)
190b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
191b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (reg >= SP1_REG)	      /* control reg */
192b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return 0x0000;
19363b917678fe6d63e633462b5be5a309511bcf3caMike Dunn
19463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	else if (reg >= DPR)	      /* parallel port reg (7715 only) */
195b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return 0x0100;
19663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn
19763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	else			      /* serial port reg */
19863b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		return (serial_portnum + 2) << 8;
199b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
200b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
201b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/*
202b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * Write data byte to the specified device register.  The data is embedded in
20363b917678fe6d63e633462b5be5a309511bcf3caMike Dunn * the value field of the setup packet. serial_portnum is ignored for registers
20463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn * not specific to a particular serial port.
205b69578df7e98659b7d94c905971a6d1025b431adMike Dunn */
20663b917678fe6d63e633462b5be5a309511bcf3caMike Dunnstatic int write_mos_reg(struct usb_serial *serial, unsigned int serial_portnum,
20763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn			 enum mos_regs reg, __u8 data)
208b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
209b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct usb_device *usbdev = serial->dev;
210b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	unsigned int pipe = usb_sndctrlpipe(usbdev, 0);
211b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	__u8 request = (__u8)0x0e;
212b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	__u8 requesttype = (__u8)0x40;
213b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	__u16 index = get_reg_index(reg);
21463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	__u16 value = get_reg_value(reg, serial_portnum) + data;
21563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	int status = usb_control_msg(usbdev, pipe, request, requesttype, value,
21663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn				     index, NULL, 0, MOS_WDR_TIMEOUT);
217b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (status < 0)
218b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dev_err(&usbdev->dev,
219b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			"mos7720: usb_control_msg() failed: %d", status);
220b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return status;
221b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
222b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
223b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/*
224b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * Read data byte from the specified device register.  The data returned by the
22563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn * device is embedded in the value field of the setup packet.  serial_portnum is
22663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn * ignored for registers that are not specific to a particular serial port.
227b69578df7e98659b7d94c905971a6d1025b431adMike Dunn */
22863b917678fe6d63e633462b5be5a309511bcf3caMike Dunnstatic int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum,
22963b917678fe6d63e633462b5be5a309511bcf3caMike Dunn			enum mos_regs reg, __u8 *data)
230b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
23163b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	struct usb_device *usbdev = serial->dev;
232b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	unsigned int pipe = usb_rcvctrlpipe(usbdev, 0);
233b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	__u8 request = (__u8)0x0d;
234b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	__u8 requesttype = (__u8)0xc0;
235b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	__u16 index = get_reg_index(reg);
23663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	__u16 value = get_reg_value(reg, serial_portnum);
237b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	int status = usb_control_msg(usbdev, pipe, request, requesttype, value,
23863b917678fe6d63e633462b5be5a309511bcf3caMike Dunn				     index, data, 1, MOS_WDR_TIMEOUT);
239b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (status < 0)
240b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dev_err(&usbdev->dev,
241b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			"mos7720: usb_control_msg() failed: %d", status);
242b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return status;
243b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
244b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
24563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
24663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn
247b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic inline int mos7715_change_mode(struct mos7715_parport *mos_parport,
248b69578df7e98659b7d94c905971a6d1025b431adMike Dunn				      enum mos7715_pp_modes mode)
249b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
250b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->shadowECR = mode;
25163b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(mos_parport->serial, dummy, ECR, mos_parport->shadowECR);
252b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return 0;
253b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
254b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
255b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void destroy_mos_parport(struct kref *kref)
256b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
257b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport =
258b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		container_of(kref, struct mos7715_parport, ref_count);
259b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
260b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
261b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	kfree(mos_parport);
262b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
263b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
264b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void destroy_urbtracker(struct kref *kref)
265b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
266b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct urbtracker *urbtrack =
267b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		container_of(kref, struct urbtracker, ref_count);
268b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = urbtrack->mos_parport;
269b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
270b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	usb_free_urb(urbtrack->urb);
271b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	kfree(urbtrack);
272b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	kref_put(&mos_parport->ref_count, destroy_mos_parport);
273b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
274b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
275b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/*
276b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * This runs as a tasklet when sending an urb in a non-blocking parallel
277b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * port callback had to be deferred because the disconnect mutex could not be
278b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * obtained at the time.
279b69578df7e98659b7d94c905971a6d1025b431adMike Dunn */
280b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void send_deferred_urbs(unsigned long _mos_parport)
281b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
282b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	int ret_val;
283b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	unsigned long flags;
284b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = (void *)_mos_parport;
285b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct urbtracker *urbtrack;
286b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct list_head *cursor, *next;
287b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
288b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
289b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
290b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* if release function ran, game over */
291b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (unlikely(mos_parport->serial == NULL))
292b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return;
293b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
294b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* try again to get the mutex */
295b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (!mutex_trylock(&mos_parport->serial->disc_mutex)) {
296b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dbg("%s: rescheduling tasklet", __func__);
297b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		tasklet_schedule(&mos_parport->urb_tasklet);
298b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return;
299b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
300b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
301b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* if device disconnected, game over */
302b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (unlikely(mos_parport->serial->disconnected)) {
303b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		mutex_unlock(&mos_parport->serial->disc_mutex);
304b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return;
305b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
306b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
307b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_lock_irqsave(&mos_parport->listlock, flags);
308b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (list_empty(&mos_parport->deferred_urbs)) {
309b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_unlock_irqrestore(&mos_parport->listlock, flags);
310b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		mutex_unlock(&mos_parport->serial->disc_mutex);
311b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dbg("%s: deferred_urbs list empty", __func__);
312b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return;
313b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
314b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
315b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* move contents of deferred_urbs list to active_urbs list and submit */
316b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	list_for_each_safe(cursor, next, &mos_parport->deferred_urbs)
317b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		list_move_tail(cursor, &mos_parport->active_urbs);
318b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	list_for_each_entry(urbtrack, &mos_parport->active_urbs,
319b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			    urblist_entry) {
320b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC);
321b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dbg("%s: urb submitted", __func__);
322b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		if (ret_val) {
323b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			dev_err(&mos_parport->serial->dev->dev,
324b69578df7e98659b7d94c905971a6d1025b431adMike Dunn				"usb_submit_urb() failed: %d", ret_val);
325b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			list_del(&urbtrack->urblist_entry);
326b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			kref_put(&urbtrack->ref_count, destroy_urbtracker);
327b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		}
328b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
329b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_unlock_irqrestore(&mos_parport->listlock, flags);
330b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mutex_unlock(&mos_parport->serial->disc_mutex);
331b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
332b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
333b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/* callback for parallel port control urbs submitted asynchronously */
334b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void async_complete(struct urb *urb)
335b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
336b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct urbtracker *urbtrack = urb->context;
337b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	int status = urb->status;
338b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
339b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (unlikely(status))
340b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dbg("%s - nonzero urb status received: %d", __func__, status);
341b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
342b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* remove the urbtracker from the active_urbs list */
343b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_lock(&urbtrack->mos_parport->listlock);
344b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	list_del(&urbtrack->urblist_entry);
345b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_unlock(&urbtrack->mos_parport->listlock);
346b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	kref_put(&urbtrack->ref_count, destroy_urbtracker);
347b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
348b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
349b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic int write_parport_reg_nonblock(struct mos7715_parport *mos_parport,
350b69578df7e98659b7d94c905971a6d1025b431adMike Dunn				      enum mos_regs reg, __u8 data)
351b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
352b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct urbtracker *urbtrack;
353b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	int ret_val;
354b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	unsigned long flags;
355b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct usb_ctrlrequest setup;
356b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct usb_serial *serial = mos_parport->serial;
357b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct usb_device *usbdev = serial->dev;
358b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
359b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
360b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* create and initialize the control urb and containing urbtracker */
361b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	urbtrack = kmalloc(sizeof(struct urbtracker), GFP_ATOMIC);
362b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (urbtrack == NULL) {
363b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dev_err(&usbdev->dev, "out of memory");
364b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return -ENOMEM;
365b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
366b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	kref_get(&mos_parport->ref_count);
367b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	urbtrack->mos_parport = mos_parport;
368b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	urbtrack->urb = usb_alloc_urb(0, GFP_ATOMIC);
369b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (urbtrack->urb == NULL) {
370b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dev_err(&usbdev->dev, "out of urbs");
371b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		kfree(urbtrack);
372b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return -ENOMEM;
373b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
374b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	setup.bRequestType = (__u8)0x40;
375b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	setup.bRequest = (__u8)0x0e;
37663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	setup.wValue = get_reg_value(reg, dummy);
377b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	setup.wIndex = get_reg_index(reg);
378b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	setup.wLength = 0;
379b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	usb_fill_control_urb(urbtrack->urb, usbdev,
380b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			     usb_sndctrlpipe(usbdev, 0),
381b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			     (unsigned char *)&setup,
382b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			     NULL, 0, async_complete, urbtrack);
383b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	kref_init(&urbtrack->ref_count);
384b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	INIT_LIST_HEAD(&urbtrack->urblist_entry);
385b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
386b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/*
387b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	 * get the disconnect mutex, or add tracker to the deferred_urbs list
388b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	 * and schedule a tasklet to try again later
389b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	 */
390b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (!mutex_trylock(&serial->disc_mutex)) {
391b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_lock_irqsave(&mos_parport->listlock, flags);
392b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		list_add_tail(&urbtrack->urblist_entry,
393b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			      &mos_parport->deferred_urbs);
394b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_unlock_irqrestore(&mos_parport->listlock, flags);
395b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		tasklet_schedule(&mos_parport->urb_tasklet);
396b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dbg("tasklet scheduled");
397b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return 0;
398b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
399b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
400b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* bail if device disconnected */
401b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (serial->disconnected) {
402b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		kref_put(&urbtrack->ref_count, destroy_urbtracker);
403b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		mutex_unlock(&serial->disc_mutex);
404b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return -ENODEV;
405b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
406b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
407b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* add the tracker to the active_urbs list and submit */
408b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_lock_irqsave(&mos_parport->listlock, flags);
409b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	list_add_tail(&urbtrack->urblist_entry, &mos_parport->active_urbs);
410b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_unlock_irqrestore(&mos_parport->listlock, flags);
411b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC);
412b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mutex_unlock(&serial->disc_mutex);
413b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (ret_val) {
414b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dev_err(&usbdev->dev,
415b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			"%s: submit_urb() failed: %d", __func__, ret_val);
416b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_lock_irqsave(&mos_parport->listlock, flags);
417b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		list_del(&urbtrack->urblist_entry);
418b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_unlock_irqrestore(&mos_parport->listlock, flags);
419b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		kref_put(&urbtrack->ref_count, destroy_urbtracker);
420b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return ret_val;
421b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
422b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return 0;
423b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
424b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
425b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/*
426b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * This is the the common top part of all parallel port callback operations that
427b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * send synchronous messages to the device.  This implements convoluted locking
428b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * that avoids two scenarios: (1) a port operation is called after usbserial
429b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * has called our release function, at which point struct mos7715_parport has
430b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * been destroyed, and (2) the device has been disconnected, but usbserial has
431b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * not called the release function yet because someone has a serial port open.
432b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * The shared release_lock prevents the first, and the mutex and disconnected
433b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * flag maintained by usbserial covers the second.  We also use the msg_pending
434b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * flag to ensure that all synchronous usb messgage calls have completed before
435b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * our release function can return.
436b69578df7e98659b7d94c905971a6d1025b431adMike Dunn */
437b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic int parport_prologue(struct parport *pp)
438b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
439b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport;
440b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
441b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_lock(&release_lock);
442b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport = pp->private_data;
443b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (unlikely(mos_parport == NULL)) {
444b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		/* release fn called, port struct destroyed */
445b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_unlock(&release_lock);
446b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return -1;
447b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
448b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->msg_pending = true;   /* synch usb call pending */
449b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	INIT_COMPLETION(mos_parport->syncmsg_compl);
450b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_unlock(&release_lock);
451b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
452b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mutex_lock(&mos_parport->serial->disc_mutex);
453b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (mos_parport->serial->disconnected) {
454b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		/* device disconnected */
455b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		mutex_unlock(&mos_parport->serial->disc_mutex);
456b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		mos_parport->msg_pending = false;
457b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		complete(&mos_parport->syncmsg_compl);
458b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return -1;
459b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
460b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
461b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return 0;
462b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
463b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
464b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/*
465b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * This is the the common bottom part of all parallel port functions that send
466b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * synchronous messages to the device.
467b69578df7e98659b7d94c905971a6d1025b431adMike Dunn */
468b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic inline void parport_epilogue(struct parport *pp)
469b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
470b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = pp->private_data;
471b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mutex_unlock(&mos_parport->serial->disc_mutex);
472b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->msg_pending = false;
473b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	complete(&mos_parport->syncmsg_compl);
474b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
475b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
476b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void parport_mos7715_write_data(struct parport *pp, unsigned char d)
477b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
478b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = pp->private_data;
479b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called: %2.2x", __func__, d);
480b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (parport_prologue(pp) < 0)
481b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return;
482b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos7715_change_mode(mos_parport, SPP);
48363b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(mos_parport->serial, dummy, DPR, (__u8)d);
484b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	parport_epilogue(pp);
485b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
486b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
487b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic unsigned char parport_mos7715_read_data(struct parport *pp)
488b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
489b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = pp->private_data;
490b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	unsigned char d;
491b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
492b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (parport_prologue(pp) < 0)
493b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return 0;
49463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	read_mos_reg(mos_parport->serial, dummy, DPR, &d);
495b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	parport_epilogue(pp);
496b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return d;
497b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
498b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
499b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void parport_mos7715_write_control(struct parport *pp, unsigned char d)
500b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
501b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = pp->private_data;
502b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	__u8 data;
503b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called: %2.2x", __func__, d);
504b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (parport_prologue(pp) < 0)
505b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return;
506b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	data = ((__u8)d & 0x0f) | (mos_parport->shadowDCR & 0xf0);
50763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(mos_parport->serial, dummy, DCR, data);
508b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->shadowDCR = data;
509b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	parport_epilogue(pp);
510b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
511b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
512b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic unsigned char parport_mos7715_read_control(struct parport *pp)
513b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
514b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = pp->private_data;
515b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	__u8 dcr;
516b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
517b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_lock(&release_lock);
518b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport = pp->private_data;
519b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (unlikely(mos_parport == NULL)) {
520b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_unlock(&release_lock);
521b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return 0;
522b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
523b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dcr = mos_parport->shadowDCR & 0x0f;
524b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_unlock(&release_lock);
525b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return dcr;
526b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
527b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
528b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic unsigned char parport_mos7715_frob_control(struct parport *pp,
529b69578df7e98659b7d94c905971a6d1025b431adMike Dunn						  unsigned char mask,
530b69578df7e98659b7d94c905971a6d1025b431adMike Dunn						  unsigned char val)
531b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
532b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = pp->private_data;
533b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	__u8 dcr;
534b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
535b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mask &= 0x0f;
536b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	val &= 0x0f;
537b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (parport_prologue(pp) < 0)
538b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return 0;
539b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->shadowDCR = (mos_parport->shadowDCR & (~mask)) ^ val;
54063b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(mos_parport->serial, dummy, DCR, mos_parport->shadowDCR);
541b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dcr = mos_parport->shadowDCR & 0x0f;
542b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	parport_epilogue(pp);
543b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return dcr;
544b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
545b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
546b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic unsigned char parport_mos7715_read_status(struct parport *pp)
547b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
548b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	unsigned char status;
549b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = pp->private_data;
550b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
551b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_lock(&release_lock);
552b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport = pp->private_data;
553b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (unlikely(mos_parport == NULL)) {	/* release called */
554b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_unlock(&release_lock);
555b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return 0;
556b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
557b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	status = atomic_read(&mos_parport->shadowDSR) & 0xf8;
558b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_unlock(&release_lock);
559b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return status;
560b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
561b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
562b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void parport_mos7715_enable_irq(struct parport *pp)
563b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
564b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
565b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
566b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void parport_mos7715_disable_irq(struct parport *pp)
567b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
568b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
569b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
570b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
571b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void parport_mos7715_data_forward(struct parport *pp)
572b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
573b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = pp->private_data;
574b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
575b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (parport_prologue(pp) < 0)
576b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return;
577b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos7715_change_mode(mos_parport, PS2);
578b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->shadowDCR &=  ~0x20;
57963b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(mos_parport->serial, dummy, DCR, mos_parport->shadowDCR);
580b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	parport_epilogue(pp);
581b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
582b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
583b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void parport_mos7715_data_reverse(struct parport *pp)
584b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
585b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = pp->private_data;
586b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
587b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (parport_prologue(pp) < 0)
588b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return;
589b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos7715_change_mode(mos_parport, PS2);
590b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->shadowDCR |= 0x20;
59163b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(mos_parport->serial, dummy, DCR, mos_parport->shadowDCR);
592b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	parport_epilogue(pp);
593b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
594b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
595b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void parport_mos7715_init_state(struct pardevice *dev,
596b69578df7e98659b7d94c905971a6d1025b431adMike Dunn				       struct parport_state *s)
597b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
598b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
599b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	s->u.pc.ctr = DCR_INIT_VAL;
600b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	s->u.pc.ecr = ECR_INIT_VAL;
601b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
602b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
603b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/* N.B. Parport core code requires that this function not block */
604b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void parport_mos7715_save_state(struct parport *pp,
605b69578df7e98659b7d94c905971a6d1025b431adMike Dunn				       struct parport_state *s)
606b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
607b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport;
608b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
609b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_lock(&release_lock);
610b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport = pp->private_data;
611b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (unlikely(mos_parport == NULL)) {	/* release called */
612b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_unlock(&release_lock);
613b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return;
614b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
615b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	s->u.pc.ctr = mos_parport->shadowDCR;
616b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	s->u.pc.ecr = mos_parport->shadowECR;
617b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_unlock(&release_lock);
618b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
619b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
620b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/* N.B. Parport core code requires that this function not block */
621b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic void parport_mos7715_restore_state(struct parport *pp,
622b69578df7e98659b7d94c905971a6d1025b431adMike Dunn					  struct parport_state *s)
623b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
624b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport;
625b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called", __func__);
626b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_lock(&release_lock);
627b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport = pp->private_data;
628b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (unlikely(mos_parport == NULL)) {	/* release called */
629b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_unlock(&release_lock);
630b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return;
631b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
632b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	write_parport_reg_nonblock(mos_parport, DCR, mos_parport->shadowDCR);
633b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	write_parport_reg_nonblock(mos_parport, ECR, mos_parport->shadowECR);
634b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_unlock(&release_lock);
635b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
636b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
637b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic size_t parport_mos7715_write_compat(struct parport *pp,
638b69578df7e98659b7d94c905971a6d1025b431adMike Dunn					   const void *buffer,
639b69578df7e98659b7d94c905971a6d1025b431adMike Dunn					   size_t len, int flags)
640b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
641b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	int retval;
642b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport = pp->private_data;
643b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	int actual_len;
644b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s called: %u chars", __func__, (unsigned int)len);
645b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (parport_prologue(pp) < 0)
646b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return 0;
647b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos7715_change_mode(mos_parport, PPF);
648b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	retval = usb_bulk_msg(mos_parport->serial->dev,
649b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			      usb_sndbulkpipe(mos_parport->serial->dev, 2),
650b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			      (void *)buffer, len, &actual_len,
651b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			      MOS_WDR_TIMEOUT);
652b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	parport_epilogue(pp);
653b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (retval) {
654b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dev_err(&mos_parport->serial->dev->dev,
655b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			"mos7720: usb_bulk_msg() failed: %d", retval);
656b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return 0;
657b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
658b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return actual_len;
659b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
660b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
661b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic struct parport_operations parport_mos7715_ops = {
662b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.owner =		THIS_MODULE,
663b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.write_data =		parport_mos7715_write_data,
664b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.read_data =		parport_mos7715_read_data,
665b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
666b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.write_control =	parport_mos7715_write_control,
667b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.read_control =		parport_mos7715_read_control,
668b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.frob_control =		parport_mos7715_frob_control,
669b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
670b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.read_status =		parport_mos7715_read_status,
671b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
672b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.enable_irq =		parport_mos7715_enable_irq,
673b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.disable_irq =		parport_mos7715_disable_irq,
674b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
675b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.data_forward =		parport_mos7715_data_forward,
676b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.data_reverse =		parport_mos7715_data_reverse,
677b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
678b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.init_state =		parport_mos7715_init_state,
679b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.save_state =		parport_mos7715_save_state,
680b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.restore_state =	parport_mos7715_restore_state,
681b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
682b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.compat_write_data =	parport_mos7715_write_compat,
683b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
684b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.nibble_read_data =	parport_ieee1284_read_nibble,
685b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	.byte_read_data =	parport_ieee1284_read_byte,
686b69578df7e98659b7d94c905971a6d1025b431adMike Dunn};
687b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
688b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/*
689b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * Allocate and initialize parallel port control struct, initialize
690b69578df7e98659b7d94c905971a6d1025b431adMike Dunn * the parallel port hardware device, and register with the parport subsystem.
691b69578df7e98659b7d94c905971a6d1025b431adMike Dunn */
692b69578df7e98659b7d94c905971a6d1025b431adMike Dunnstatic int mos7715_parport_init(struct usb_serial *serial)
693b69578df7e98659b7d94c905971a6d1025b431adMike Dunn{
694b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	struct mos7715_parport *mos_parport;
695b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
696b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* allocate and initialize parallel port control struct */
697b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL);
698b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (mos_parport == NULL) {
699b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dbg("mos7715_parport_init: kzalloc failed");
700b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return -ENOMEM;
701b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
702b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->msg_pending = false;
703b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	kref_init(&mos_parport->ref_count);
704b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	spin_lock_init(&mos_parport->listlock);
705b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	INIT_LIST_HEAD(&mos_parport->active_urbs);
706b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	INIT_LIST_HEAD(&mos_parport->deferred_urbs);
707b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	usb_set_serial_data(serial, mos_parport); /* hijack private pointer */
708b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->serial = serial;
709b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	tasklet_init(&mos_parport->urb_tasklet, send_deferred_urbs,
710b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		     (unsigned long) mos_parport);
711b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	init_completion(&mos_parport->syncmsg_compl);
712b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
713b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* cycle parallel port reset bit */
71463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(mos_parport->serial, dummy, PP_REG, (__u8)0x80);
71563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(mos_parport->serial, dummy, PP_REG, (__u8)0x00);
716b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
717b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* initialize device registers */
718b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->shadowDCR = DCR_INIT_VAL;
71963b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(mos_parport->serial, dummy, DCR, mos_parport->shadowDCR);
720b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->shadowECR = ECR_INIT_VAL;
72163b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(mos_parport->serial, dummy, ECR, mos_parport->shadowECR);
722b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
723b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* register with parport core */
724b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->pp = parport_register_port(0, PARPORT_IRQ_NONE,
725b69578df7e98659b7d94c905971a6d1025b431adMike Dunn						PARPORT_DMA_NONE,
726b69578df7e98659b7d94c905971a6d1025b431adMike Dunn						&parport_mos7715_ops);
727b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (mos_parport->pp == NULL) {
728b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dev_err(&serial->interface->dev,
729b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			"Could not register parport\n");
730b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		kref_put(&mos_parport->ref_count, destroy_mos_parport);
731b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		return -EIO;
732b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
733b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->pp->private_data = mos_parport;
734b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->pp->modes = PARPORT_MODE_COMPAT | PARPORT_MODE_PCSPP;
735b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	mos_parport->pp->dev = &serial->interface->dev;
736b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	parport_announce_port(mos_parport->pp);
737b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
738b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	return 0;
739b69578df7e98659b7d94c905971a6d1025b431adMike Dunn}
740b69578df7e98659b7d94c905971a6d1025b431adMike Dunn#endif	/* CONFIG_USB_SERIAL_MOS7715_PARPORT */
7410f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
7420f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
7430f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * mos7720_interrupt_callback
7440f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	this is the callback function for when we have received data on the
7450f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	interrupt endpoint.
7460f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman */
7470f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanstatic void mos7720_interrupt_callback(struct urb *urb)
7480f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
7490f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int result;
7500f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int length;
75181105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman	int status = urb->status;
752325b70c233396f0cfe15012682a5080bf8040901Oliver Neukum	__u8 *data;
7530f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u8 sp1;
7540f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u8 sp2;
7550f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
75681105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman	switch (status) {
7570f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case 0:
7580f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		/* success */
7590f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		break;
7600f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case -ECONNRESET:
7610f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case -ENOENT:
7620f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case -ESHUTDOWN:
7630f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		/* this urb is terminated, clean up */
764441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - urb shutting down with status: %d", __func__,
76581105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman		    status);
7660f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
7670f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	default:
768441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - nonzero urb status received: %d", __func__,
76981105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman		    status);
7700f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		goto exit;
7710f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
7720f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
7730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	length = urb->actual_length;
7740f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	data = urb->transfer_buffer;
7750f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
7760f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Moschip get 4 bytes
7770f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	 * Byte 1 IIR Port 1 (port.number is 0)
7780f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	 * Byte 2 IIR Port 2 (port.number is 1)
7790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	 * Byte 3 --------------
7800f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	 * Byte 4 FIFO status for both */
781325b70c233396f0cfe15012682a5080bf8040901Oliver Neukum
782325b70c233396f0cfe15012682a5080bf8040901Oliver Neukum	/* the above description is inverted
783325b70c233396f0cfe15012682a5080bf8040901Oliver Neukum	 * 	oneukum 2007-03-14 */
784325b70c233396f0cfe15012682a5080bf8040901Oliver Neukum
785325b70c233396f0cfe15012682a5080bf8040901Oliver Neukum	if (unlikely(length != 4)) {
7860f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		dbg("Wrong data !!!");
7870f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
7880f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
7890f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
790325b70c233396f0cfe15012682a5080bf8040901Oliver Neukum	sp1 = data[3];
791325b70c233396f0cfe15012682a5080bf8040901Oliver Neukum	sp2 = data[2];
7920f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
793325b70c233396f0cfe15012682a5080bf8040901Oliver Neukum	if ((sp1 | sp2) & 0x01) {
7940f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		/* No Interrupt Pending in both the ports */
7950f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		dbg("No Interrupt !!!");
7960f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	} else {
7970f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		switch (sp1 & 0x0f) {
7980f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		case SERIAL_IIR_RLS:
7990f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			dbg("Serial Port 1: Receiver status error or address "
8000f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			    "bit detected in 9-bit mode\n");
8010f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			break;
8020f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		case SERIAL_IIR_CTI:
8030f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			dbg("Serial Port 1: Receiver time out");
8040f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			break;
8050f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		case SERIAL_IIR_MS:
806b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			/* dbg("Serial Port 1: Modem status change"); */
8070f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			break;
8080f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		}
8090f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
8100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		switch (sp2 & 0x0f) {
8110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		case SERIAL_IIR_RLS:
8120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			dbg("Serial Port 2: Receiver status error or address "
8130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			    "bit detected in 9-bit mode");
8140f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			break;
8150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		case SERIAL_IIR_CTI:
8160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			dbg("Serial Port 2: Receiver time out");
8170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			break;
8180f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		case SERIAL_IIR_MS:
819b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			/* dbg("Serial Port 2: Modem status change"); */
8200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			break;
8210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		}
8220f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
8230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
8240f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanexit:
8250f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	result = usb_submit_urb(urb, GFP_ATOMIC);
8260f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (result)
8270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		dev_err(&urb->dev->dev,
8280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			"%s - Error %d submitting control urb\n",
829441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison			__func__, result);
8300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
8310f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
8320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
833fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn * mos7715_interrupt_callback
834fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn *	this is the 7715's callback function for when we have received data on
835fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn *	the interrupt endpoint.
836fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn */
837fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunnstatic void mos7715_interrupt_callback(struct urb *urb)
838fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn{
839fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	int result;
840fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	int length;
841fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	int status = urb->status;
842fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	__u8 *data;
843fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	__u8 iir;
844fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
845fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	switch (status) {
846fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	case 0:
847fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		/* success */
848fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		break;
849fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	case -ECONNRESET:
850fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	case -ENOENT:
851fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	case -ESHUTDOWN:
852b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	case -ENODEV:
853fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		/* this urb is terminated, clean up */
854fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		dbg("%s - urb shutting down with status: %d", __func__,
855fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		    status);
856fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		return;
857fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	default:
858fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		dbg("%s - nonzero urb status received: %d", __func__,
859fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		    status);
860fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		goto exit;
861fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	}
862fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
863fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	length = urb->actual_length;
864fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	data = urb->transfer_buffer;
865fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
866fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	/* Structure of data from 7715 device:
867fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * Byte 1: IIR serial Port
868fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * Byte 2: unused
869fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * Byte 2: DSR parallel port
870fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * Byte 4: FIFO status for both */
871fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
872fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	if (unlikely(length != 4)) {
873fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		dbg("Wrong data !!!");
874fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		return;
875fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	}
876fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
877fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	iir = data[0];
878fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	if (!(iir & 0x01)) {	/* serial port interrupt pending */
879fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		switch (iir & 0x0f) {
880fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		case SERIAL_IIR_RLS:
881fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			dbg("Serial Port: Receiver status error or address "
882fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			    "bit detected in 9-bit mode\n");
883fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			break;
884fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		case SERIAL_IIR_CTI:
885fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			dbg("Serial Port: Receiver time out");
886fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			break;
887fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		case SERIAL_IIR_MS:
888b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			/* dbg("Serial Port: Modem status change"); */
889fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			break;
890fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		}
891fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	}
892fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
893b69578df7e98659b7d94c905971a6d1025b431adMike Dunn#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
894b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	{       /* update local copy of DSR reg */
895b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		struct usb_serial_port *port = urb->context;
896b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		struct mos7715_parport *mos_parport = port->serial->private;
897b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		if (unlikely(mos_parport == NULL))
898b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			return;
899b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		atomic_set(&mos_parport->shadowDSR, data[2]);
900b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
901b69578df7e98659b7d94c905971a6d1025b431adMike Dunn#endif
902b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
903fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunnexit:
904fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	result = usb_submit_urb(urb, GFP_ATOMIC);
905fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	if (result)
906fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		dev_err(&urb->dev->dev,
907fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			"%s - Error %d submitting control urb\n",
908fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			__func__, result);
909fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn}
910fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
911fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn/*
9120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * mos7720_bulk_in_callback
9130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	this is the callback function for when we have received data on the
9140f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	bulk in endpoint.
9150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman */
9160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanstatic void mos7720_bulk_in_callback(struct urb *urb)
9170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
91881105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman	int retval;
9190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	unsigned char *data ;
9200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial_port *port;
9210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct tty_struct *tty;
92281105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman	int status = urb->status;
9230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
92481105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman	if (status) {
92581105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman		dbg("nonzero read bulk status received: %d", status);
9260f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
9270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
9280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
929b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	port = urb->context;
9300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
931441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("Entering...%s", __func__);
9320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
9330f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	data = urb->transfer_buffer;
9340f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
9354a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox	tty = tty_port_tty_get(&port->port);
9360f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (tty && urb->actual_length) {
9370f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		tty_insert_flip_string(tty, data, urb->actual_length);
9380f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		tty_flip_buffer_push(tty);
9390f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
9404a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox	tty_kref_put(tty);
9410f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
9420f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (port->read_urb->status != -EINPROGRESS) {
94381105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman		retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
94481105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman		if (retval)
94581105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman			dbg("usb_submit_urb(read bulk) failed, retval = %d",
94681105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman			    retval);
9470f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
9480f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
9490f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
9500f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
9510f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * mos7720_bulk_out_data_callback
9520f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	this is the callback function for when we have finished sending serial
9530f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	data on the bulk out endpoint.
9540f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman */
9550f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanstatic void mos7720_bulk_out_data_callback(struct urb *urb)
9560f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
9570f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port;
9580f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct tty_struct *tty;
95981105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman	int status = urb->status;
9600f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
96181105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman	if (status) {
96281105984848481d8876e454e3c503dbd0e8e4dceGreg Kroah-Hartman		dbg("nonzero write bulk status received:%d", status);
9630f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
9640f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
9650f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
9660f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port = urb->context;
9670f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (!mos7720_port) {
9680f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		dbg("NULL mos7720_port pointer");
9690f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return ;
9700f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
9710f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
9724a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox	tty = tty_port_tty_get(&mos7720_port->port->port);
9730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
974b963a8441cb95999c97bea379607071a869c65f0Jiri Slaby	if (tty && mos7720_port->open)
975b963a8441cb95999c97bea379607071a869c65f0Jiri Slaby		tty_wakeup(tty);
9764a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox	tty_kref_put(tty);
9770f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
9780f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
9790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
980fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn * mos77xx_probe
981fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn *	this function installs the appropriate read interrupt endpoint callback
982fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn *	depending on whether the device is a 7720 or 7715, thus avoiding costly
983fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn *	run-time checks in the high-frequency callback routine itself.
984fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn */
985fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunnstatic int mos77xx_probe(struct usb_serial *serial,
986fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			 const struct usb_device_id *id)
987fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn{
988fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	if (id->idProduct == MOSCHIP_DEVICE_ID_7715)
989fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		moschip7720_2port_driver.read_int_callback =
990fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			mos7715_interrupt_callback;
991fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	else
992fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		moschip7720_2port_driver.read_int_callback =
993fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			mos7720_interrupt_callback;
994fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
995fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	return 0;
996fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn}
997fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
998fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunnstatic int mos77xx_calc_num_ports(struct usb_serial *serial)
999fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn{
1000fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
1001fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	if (product == MOSCHIP_DEVICE_ID_7715)
1002fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		return 1;
1003fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
1004fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	return 2;
1005fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn}
1006fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
1007a509a7e478e4766114d69f12d19d644ac63e9765Alan Coxstatic int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port)
10080f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
10090f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial *serial;
10100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct urb *urb;
10110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port;
10120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int response;
10130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int port_number;
101463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	__u8 data;
1015fe4b65ec9127a336eeaa503f878062d9e6f44591Oliver Neukum	int allocated_urbs = 0;
10160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int j;
10170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
10180f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	serial = port->serial;
10190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
10200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port = usb_get_serial_port_data(port);
10210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL)
10220f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -ENODEV;
10230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
10240f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	usb_clear_halt(serial->dev, port->write_urb->pipe);
10250f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	usb_clear_halt(serial->dev, port->read_urb->pipe);
10260f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
10270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Initialising the write urb pool */
10280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	for (j = 0; j < NUM_URBS; ++j) {
10294da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		urb = usb_alloc_urb(0, GFP_KERNEL);
10300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		mos7720_port->write_urb_pool[j] = urb;
10310f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
10320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (urb == NULL) {
1033194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			dev_err(&port->dev, "No more urbs???\n");
10340f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			continue;
10350f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		}
10360f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
10370f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
10380f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman					       GFP_KERNEL);
10390f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (!urb->transfer_buffer) {
1040194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			dev_err(&port->dev,
1041194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman				"%s-out of memory for urb buffers.\n",
1042194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman				__func__);
1043fe4b65ec9127a336eeaa503f878062d9e6f44591Oliver Neukum			usb_free_urb(mos7720_port->write_urb_pool[j]);
1044fe4b65ec9127a336eeaa503f878062d9e6f44591Oliver Neukum			mos7720_port->write_urb_pool[j] = NULL;
10450f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			continue;
10460f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		}
1047fe4b65ec9127a336eeaa503f878062d9e6f44591Oliver Neukum		allocated_urbs++;
10480f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
10490f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1050fe4b65ec9127a336eeaa503f878062d9e6f44591Oliver Neukum	if (!allocated_urbs)
1051fe4b65ec9127a336eeaa503f878062d9e6f44591Oliver Neukum		return -ENOMEM;
1052fe4b65ec9127a336eeaa503f878062d9e6f44591Oliver Neukum
10530f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	 /* Initialize MCS7720 -- Write Init values to corresponding Registers
10540f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	  *
10550f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	  * Register Index
10562f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers	  * 0 : THR/RHR
10570f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	  * 1 : IER
10580f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	  * 2 : FCR
10590f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	  * 3 : LCR
10600f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	  * 4 : MCR
10612f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers	  * 5 : LSR
10622f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers	  * 6 : MSR
10632f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers	  * 7 : SPR
10640f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	  *
10650f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	  * 0x08 : SP1/2 Control Reg
10660f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	  */
10670f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	port_number = port->number - port->serial->minor;
106863b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	read_mos_reg(serial, port_number, LSR, &data);
106963b917678fe6d63e633462b5be5a309511bcf3caMike Dunn
1070759f3634267a67ac90f3fa7fc06510dfd43b4e45Joe Perches	dbg("SS::%p LSR:%x", mos7720_port, data);
10710f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
10720f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	dbg("Check:Sending Command ..........");
10730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
107463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, dummy, SP1_REG, 0x02);
107563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, dummy, SP2_REG, 0x02);
10760f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
107763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, IER, 0x00);
107863b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, FCR, 0x00);
10790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
108063b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, FCR, 0xcf);
108163b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	mos7720_port->shadowLCR = 0x03;
108263b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR);
108363b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	mos7720_port->shadowMCR = 0x0b;
108463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR);
10850f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
108663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, SP_CONTROL_REG, 0x00);
108763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	read_mos_reg(serial, dummy, SP_CONTROL_REG, &data);
10880f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	data = data | (port->number - port->serial->minor + 1);
108963b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, dummy, SP_CONTROL_REG, data);
109063b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	mos7720_port->shadowLCR = 0x83;
109163b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR);
109263b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, THR, 0x0c);
109363b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, IER, 0x00);
109463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	mos7720_port->shadowLCR = 0x03;
109563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR);
109663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, IER, 0x0c);
10970f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
10980f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	response = usb_submit_urb(port->read_urb, GFP_KERNEL);
10990f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (response)
11004da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		dev_err(&port->dev, "%s - Error %d submitting read urb\n",
11014da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox							__func__, response);
11020f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11030f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* initialize our icount structure */
11040f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	memset(&(mos7720_port->icount), 0x00, sizeof(mos7720_port->icount));
11050f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11060f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* initialize our port settings */
11070f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port->shadowMCR = UART_MCR_OUT2; /* Must set to enable ints! */
11080f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11090f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* send a open port command */
11100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port->open = 1;
11110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return 0;
11130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
11140f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
11160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * mos7720_chars_in_buffer
11170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	this function is called by the tty driver when it wants to know how many
11180f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	bytes of data we currently have outstanding in the port (data that has
11190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	been written, but hasn't made it out the port yet)
11200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	If successful, we return the number of bytes left to be written in the
11210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	system,
11220f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	Otherwise we return a negative error number.
11230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman */
112495da310e66ee8090119596c70ca8432e57f9a97fAlan Coxstatic int mos7720_chars_in_buffer(struct tty_struct *tty)
11250f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
112695da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	struct usb_serial_port *port = tty->driver_data;
11270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int i;
11280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int chars = 0;
11290f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port;
11300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1131441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s:entering ...........", __func__);
11320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11330f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port = usb_get_serial_port_data(port);
11340f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL) {
1135441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s:leaving ...........", __func__);
113623198fda7182969b619613a555f8645fdc3dc334Alan Cox		return 0;
11370f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
11380f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11390f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	for (i = 0; i < NUM_URBS; ++i) {
11404da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		if (mos7720_port->write_urb_pool[i] &&
11414da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		    mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
11420f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			chars += URB_TRANSFER_BUFFER_SIZE;
11430f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
1144441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - returns %d", __func__, chars);
11450f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return chars;
11460f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
11470f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1148335f8514f200e63d689113d29cb7253a5c282967Alan Coxstatic void mos7720_close(struct usb_serial_port *port)
11490f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
11500f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial *serial;
11510f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port;
11520f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int j;
11530f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11540f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	dbg("mos7720_close:entering...");
11550f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11560f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	serial = port->serial;
11570f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11580f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port = usb_get_serial_port_data(port);
11590f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL)
11600f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
11610f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11620f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	for (j = 0; j < NUM_URBS; ++j)
11630f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		usb_kill_urb(mos7720_port->write_urb_pool[j]);
11640f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11650f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Freeing Write URBs */
11660f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	for (j = 0; j < NUM_URBS; ++j) {
11670f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (mos7720_port->write_urb_pool[j]) {
11680f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			kfree(mos7720_port->write_urb_pool[j]->transfer_buffer);
11690f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			usb_free_urb(mos7720_port->write_urb_pool[j]);
11700f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		}
11710f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
11720f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
11730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* While closing port, shutdown all bulk read, write  *
1174a1cd7e99b343543af2be4c8c5755e26f6bfd725aOliver Neukum	 * and interrupt read if they exists, otherwise nop   */
1175a1cd7e99b343543af2be4c8c5755e26f6bfd725aOliver Neukum	dbg("Shutdown bulk write");
1176a1cd7e99b343543af2be4c8c5755e26f6bfd725aOliver Neukum	usb_kill_urb(port->write_urb);
1177a1cd7e99b343543af2be4c8c5755e26f6bfd725aOliver Neukum	dbg("Shutdown bulk read");
1178a1cd7e99b343543af2be4c8c5755e26f6bfd725aOliver Neukum	usb_kill_urb(port->read_urb);
1179a1cd7e99b343543af2be4c8c5755e26f6bfd725aOliver Neukum
1180a1cd7e99b343543af2be4c8c5755e26f6bfd725aOliver Neukum	mutex_lock(&serial->disc_mutex);
1181a1cd7e99b343543af2be4c8c5755e26f6bfd725aOliver Neukum	/* these commands must not be issued if the device has
1182a1cd7e99b343543af2be4c8c5755e26f6bfd725aOliver Neukum	 * been disconnected */
1183a1cd7e99b343543af2be4c8c5755e26f6bfd725aOliver Neukum	if (!serial->disconnected) {
118463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		write_mos_reg(serial, port->number - port->serial->minor,
118563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn			      MCR, 0x00);
118663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		write_mos_reg(serial, port->number - port->serial->minor,
118763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn			      IER, 0x00);
11880f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
1189a1cd7e99b343543af2be4c8c5755e26f6bfd725aOliver Neukum	mutex_unlock(&serial->disc_mutex);
11900f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port->open = 0;
11910f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1192441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("Leaving %s", __func__);
11930f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
11940f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
119595da310e66ee8090119596c70ca8432e57f9a97fAlan Coxstatic void mos7720_break(struct tty_struct *tty, int break_state)
11960f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
119795da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	struct usb_serial_port *port = tty->driver_data;
11984da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	unsigned char data;
11990f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial *serial;
12000f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port;
12010f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1202441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("Entering %s", __func__);
12030f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12040f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	serial = port->serial;
12050f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12060f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port = usb_get_serial_port_data(port);
12070f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL)
12080f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
12090f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (break_state == -1)
12110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		data = mos7720_port->shadowLCR | UART_LCR_SBC;
12120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	else
12130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		data = mos7720_port->shadowLCR & ~UART_LCR_SBC;
12140f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port->shadowLCR  = data;
121663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port->number - port->serial->minor,
121763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		      LCR, mos7720_port->shadowLCR);
12180f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
12190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
12210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * mos7720_write_room
12220f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	this function is called by the tty driver when it wants to know how many
12230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	bytes of data we can accept for a specific port.
12240f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	If successful, we return the amount of room that we have for this port
12250f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	Otherwise we return a negative error number.
12260f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman */
122795da310e66ee8090119596c70ca8432e57f9a97fAlan Coxstatic int mos7720_write_room(struct tty_struct *tty)
12280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
122995da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	struct usb_serial_port *port = tty->driver_data;
12300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port;
12310f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int room = 0;
12320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int i;
12330f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1234441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s:entering ...........", __func__);
12350f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12360f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port = usb_get_serial_port_data(port);
12370f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL) {
1238441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s:leaving ...........", __func__);
12390f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -ENODEV;
12400f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
12410f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1242a5b6f60c5a30c494017c7a2d11c4067f90d3d0dfAlan Cox	/* FIXME: Locking */
12430f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	for (i = 0; i < NUM_URBS; ++i) {
12444da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		if (mos7720_port->write_urb_pool[i] &&
12454da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		    mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
12460f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			room += URB_TRANSFER_BUFFER_SIZE;
12470f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
12480f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1249441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - returns %d", __func__, room);
12500f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return room;
12510f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
12520f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
125395da310e66ee8090119596c70ca8432e57f9a97fAlan Coxstatic int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port,
125495da310e66ee8090119596c70ca8432e57f9a97fAlan Cox				 const unsigned char *data, int count)
12550f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
12560f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int status;
12570f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int i;
12580f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int bytes_sent = 0;
12590f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int transfer_size;
12600f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12610f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port;
12620f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial *serial;
12630f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct urb    *urb;
12640f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	const unsigned char *current_position = data;
12650f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1266441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s:entering ...........", __func__);
12670f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12680f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	serial = port->serial;
12690f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12700f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port = usb_get_serial_port_data(port);
12710f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL) {
12720f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		dbg("mos7720_port is NULL");
12730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -ENODEV;
12740f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
12750f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12760f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* try to find a free urb in the list */
12770f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	urb = NULL;
12780f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	for (i = 0; i < NUM_URBS; ++i) {
12804da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		if (mos7720_port->write_urb_pool[i] &&
12814da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		    mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
12820f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			urb = mos7720_port->write_urb_pool[i];
12834da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox			dbg("URB:%d", i);
12840f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			break;
12850f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		}
12860f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
12870f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12880f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (urb == NULL) {
1289441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - no more free urbs", __func__);
12900f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		goto exit;
12910f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
12920f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
12930f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (urb->transfer_buffer == NULL) {
12940f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
12950f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman					       GFP_KERNEL);
12960f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (urb->transfer_buffer == NULL) {
129722a416c4e0f2179b57028e084ac0ed2c110333bdJohan Hovold			dev_err_console(port, "%s no more kernel memory...\n",
1298194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman				__func__);
12990f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			goto exit;
13000f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		}
13010f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
13024da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
13030f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13040f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	memcpy(urb->transfer_buffer, current_position, transfer_size);
1305441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	usb_serial_debug_data(debug, &port->dev, __func__, transfer_size,
13060f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			      urb->transfer_buffer);
13070f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13080f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* fill urb with data and submit  */
13090f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	usb_fill_bulk_urb(urb, serial->dev,
13100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			  usb_sndbulkpipe(serial->dev,
13114da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox					port->bulk_out_endpointAddress),
13120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			  urb->transfer_buffer, transfer_size,
13130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			  mos7720_bulk_out_data_callback, mos7720_port);
13140f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* send it down the pipe */
13164da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	status = usb_submit_urb(urb, GFP_ATOMIC);
13170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (status) {
131822a416c4e0f2179b57028e084ac0ed2c110333bdJohan Hovold		dev_err_console(port, "%s - usb_submit_urb(write bulk) failed "
1319194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			"with status = %d\n", __func__, status);
13200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		bytes_sent = status;
13210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		goto exit;
13220f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
13230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	bytes_sent = transfer_size;
13240f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13250f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanexit:
13260f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return bytes_sent;
13270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
13280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
132995da310e66ee8090119596c70ca8432e57f9a97fAlan Coxstatic void mos7720_throttle(struct tty_struct *tty)
13300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
133195da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	struct usb_serial_port *port = tty->driver_data;
13320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port;
13330f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int status;
13340f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1335759f3634267a67ac90f3fa7fc06510dfd43b4e45Joe Perches	dbg("%s- port %d", __func__, port->number);
13360f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13370f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port = usb_get_serial_port_data(port);
13380f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13390f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL)
13400f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
13410f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13420f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (!mos7720_port->open) {
13430f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		dbg("port not opened");
13440f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
13450f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
13460f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1347441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s: Entering ..........", __func__);
13480f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13490f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* if we are implementing XON/XOFF, send the stop character */
13500f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (I_IXOFF(tty)) {
13510f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		unsigned char stop_char = STOP_CHAR(tty);
135295da310e66ee8090119596c70ca8432e57f9a97fAlan Cox		status = mos7720_write(tty, port, &stop_char, 1);
13530f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (status <= 0)
13540f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			return;
13550f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
13560f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13570f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* if we are implementing RTS/CTS, toggle that line */
13580f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (tty->termios->c_cflag & CRTSCTS) {
13590f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		mos7720_port->shadowMCR &= ~UART_MCR_RTS;
136063b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		write_mos_reg(port->serial, port->number - port->serial->minor,
136163b917678fe6d63e633462b5be5a309511bcf3caMike Dunn			      MCR, mos7720_port->shadowMCR);
13620f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (status != 0)
13630f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			return;
13640f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
13650f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
13660f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
136795da310e66ee8090119596c70ca8432e57f9a97fAlan Coxstatic void mos7720_unthrottle(struct tty_struct *tty)
13680f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
136995da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	struct usb_serial_port *port = tty->driver_data;
13700f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
137195da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	int status;
13720f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL)
13740f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
13750f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13760f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (!mos7720_port->open) {
1377441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - port not opened", __func__);
13780f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
13790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
13800f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1381441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s: Entering ..........", __func__);
13820f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13830f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* if we are implementing XON/XOFF, send the start character */
13840f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (I_IXOFF(tty)) {
13850f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		unsigned char start_char = START_CHAR(tty);
138695da310e66ee8090119596c70ca8432e57f9a97fAlan Cox		status = mos7720_write(tty, port, &start_char, 1);
13870f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (status <= 0)
13880f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			return;
13890f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
13900f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
13910f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* if we are implementing RTS/CTS, toggle that line */
13920f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (tty->termios->c_cflag & CRTSCTS) {
13930f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		mos7720_port->shadowMCR |= UART_MCR_RTS;
139463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		write_mos_reg(port->serial, port->number - port->serial->minor,
139563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn			      MCR, mos7720_port->shadowMCR);
13960f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (status != 0)
13970f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			return;
13980f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
13990f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
14000f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1401b69578df7e98659b7d94c905971a6d1025b431adMike Dunn/* FIXME: this function does not work */
14020f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanstatic int set_higher_rates(struct moschip_port *mos7720_port,
14030f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			    unsigned int baud)
14040f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
14050f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial_port *port;
14060f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial *serial;
14070f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int port_number;
140863b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	enum mos_regs sp_reg;
14090f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL)
14100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -EINVAL;
14110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
14120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	port = mos7720_port->port;
14130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	serial = port->serial;
14140f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
14154da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	 /***********************************************
14164da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	 *      Init Sequence for higher rates
14174da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	 ***********************************************/
14180f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	dbg("Sending Setting Commands ..........");
14190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	port_number = port->number - port->serial->minor;
14200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
142163b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, IER, 0x00);
142263b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, FCR, 0x00);
142363b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, FCR, 0xcf);
142463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	mos7720_port->shadowMCR = 0x0b;
142563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR);
142663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, dummy, SP_CONTROL_REG, 0x00);
14270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
14284da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	/***********************************************
14294da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	 *              Set for higher rates           *
14304da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	 ***********************************************/
1431b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* writing baud rate verbatum into uart clock field clearly not right */
143263b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	if (port_number == 0)
143363b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		sp_reg = SP1_REG;
143463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	else
143563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		sp_reg = SP2_REG;
143663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, dummy, sp_reg, baud * 0x10);
143763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, dummy, SP_CONTROL_REG, 0x03);
143863b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	mos7720_port->shadowMCR = 0x2b;
143963b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR);
14400f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
14414da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	/***********************************************
14424da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	 *              Set DLL/DLM
14434da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	 ***********************************************/
144463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB;
144563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR);
144663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, DLL, 0x01);
144763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, DLM, 0x00);
144863b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
144963b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR);
14500f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
14510f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return 0;
14520f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
14530f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
14540f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/* baud rate information */
14554da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Coxstruct divisor_table_entry {
14560f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u32  baudrate;
14570f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u16  divisor;
14580f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman};
14590f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
14600f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/* Define table of divisors for moschip 7720 hardware	   *
14610f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * These assume a 3.6864MHz crystal, the standard /16, and *
14620f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * MCR.7 = 0.						   */
14630f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanstatic struct divisor_table_entry divisor_table[] = {
14640f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   50,		2304},
14650f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   110,	1047},	/* 2094.545455 => 230450   => .0217 % over */
14660f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   134,	857},	/* 1713.011152 => 230398.5 => .00065% under */
14670f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   150,	768},
14680f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   300,	384},
14690f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   600,	192},
14700f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   1200,	96},
14710f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   1800,	64},
14720f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   2400,	48},
14730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   4800,	24},
14740f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   7200,	16},
14750f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   9600,	12},
14760f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   19200,	6},
14770f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   38400,	3},
14780f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   57600,	2},
14790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	{   115200,	1},
14800f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman};
14810f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
14820f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*****************************************************************************
14830f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * calc_baud_rate_divisor
14840f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	this function calculates the proper baud rate divisor for the specified
14850f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	baud rate.
14860f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *****************************************************************************/
14870f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanstatic int calc_baud_rate_divisor(int baudrate, int *divisor)
14880f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
14890f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int i;
14900f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u16 custom;
14910f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u16 round1;
14920f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u16 round;
14930f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
14940f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1495441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - %d", __func__, baudrate);
14960f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
14970f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	for (i = 0; i < ARRAY_SIZE(divisor_table); i++) {
14980f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (divisor_table[i].baudrate == baudrate) {
14990f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			*divisor = divisor_table[i].divisor;
15000f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			return 0;
15010f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		}
15020f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
15030f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15044da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	/* After trying for all the standard baud rates    *
15054da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	 * Try calculating the divisor for this baud rate  */
15060f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (baudrate > 75 &&  baudrate < 230400) {
15070f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		/* get the divisor */
15080f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		custom = (__u16)(230400L  / baudrate);
15090f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		/* Check for round off */
15110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		round1 = (__u16)(2304000L / baudrate);
15120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		round = (__u16)(round1 - (custom * 10));
15130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (round > 4)
15140f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			custom++;
15150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		*divisor = custom;
15160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15174da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		dbg("Baud %d = %d", baudrate, custom);
15180f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return 0;
15190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
15200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	dbg("Baud calculation Failed...");
15220f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return -EINVAL;
15230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
15240f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15250f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
15260f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * send_cmd_write_baud_rate
15270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	this function sends the proper command to change the baud rate of the
15280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	specified port.
15290f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman */
15300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanstatic int send_cmd_write_baud_rate(struct moschip_port *mos7720_port,
15310f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman				    int baudrate)
15320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
15330f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial_port *port;
15340f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial *serial;
15350f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int divisor;
15360f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int status;
15370f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	unsigned char number;
15380f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15390f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL)
15400f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -1;
15410f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15420f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	port = mos7720_port->port;
15430f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	serial = port->serial;
15440f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1545441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s: Entering ..........", __func__);
15460f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15470f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	number = port->number - port->serial->minor;
1548441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port = %d, baud = %d", __func__, port->number, baudrate);
15490f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15504da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	/* Calculate the Divisor */
15510f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	status = calc_baud_rate_divisor(baudrate, &divisor);
15520f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (status) {
1553194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman		dev_err(&port->dev, "%s - bad baud rate\n", __func__);
15540f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return status;
15550f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
15560f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15574da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	/* Enable access to divisor latch */
155863b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB;
155963b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, number, LCR, mos7720_port->shadowLCR);
15600f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15610f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Write the divisor */
156263b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, number, DLL, (__u8)(divisor & 0xff));
156363b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, number, DLM, (__u8)((divisor & 0xff00) >> 8));
15640f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15654da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	/* Disable access to divisor latch */
156663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
156763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, number, LCR, mos7720_port->shadowLCR);
15680f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15690f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return status;
15700f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
15710f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15720f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
15730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * change_port_settings
15740f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	This routine is called to set the UART on the device to match
15750f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *      the specified new settings.
15760f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman */
157795da310e66ee8090119596c70ca8432e57f9a97fAlan Coxstatic void change_port_settings(struct tty_struct *tty,
157895da310e66ee8090119596c70ca8432e57f9a97fAlan Cox				 struct moschip_port *mos7720_port,
1579606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox				 struct ktermios *old_termios)
15800f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
15810f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial_port *port;
15820f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial *serial;
15830f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int baud;
15840f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	unsigned cflag;
15850f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	unsigned iflag;
15860f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u8 mask = 0xff;
15870f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u8 lData;
15880f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u8 lParity;
15890f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	__u8 lStop;
15900f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int status;
15910f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int port_number;
15920f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15930f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL)
15940f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return ;
15950f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
15960f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	port = mos7720_port->port;
15970f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	serial = port->serial;
15980f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	port_number = port->number - port->serial->minor;
15990f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1600441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port %d", __func__, port->number);
16010f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16020f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (!mos7720_port->open) {
1603441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - port not opened", __func__);
16040f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
16050f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
16060f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1607441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s: Entering ..........", __func__);
16080f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16090f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	lData = UART_LCR_WLEN8;
16100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	lStop = 0x00;	/* 1 stop bit */
16110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	lParity = 0x00;	/* No parity */
16120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	cflag = tty->termios->c_cflag;
16140f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	iflag = tty->termios->c_iflag;
16150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Change the number of bits */
16170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	switch (cflag & CSIZE) {
16180f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case CS5:
16190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		lData = UART_LCR_WLEN5;
16200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		mask = 0x1f;
16210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		break;
16220f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case CS6:
16240f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		lData = UART_LCR_WLEN6;
16250f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		mask = 0x3f;
16260f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		break;
16270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case CS7:
16290f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		lData = UART_LCR_WLEN7;
16300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		mask = 0x7f;
16310f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		break;
16320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	default:
16330f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case CS8:
16340f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		lData = UART_LCR_WLEN8;
16350f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		break;
16360f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
16370f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16380f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Change the Parity bit */
16390f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (cflag & PARENB) {
16400f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (cflag & PARODD) {
16410f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			lParity = UART_LCR_PARITY;
1642441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison			dbg("%s - parity = odd", __func__);
16430f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		} else {
16440f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			lParity = (UART_LCR_EPAR | UART_LCR_PARITY);
1645441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison			dbg("%s - parity = even", __func__);
16460f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		}
16470f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16480f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	} else {
1649441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - parity = none", __func__);
16500f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
16510f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16520f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (cflag & CMSPAR)
16530f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		lParity = lParity | 0x20;
16540f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16550f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Change the Stop bit */
16560f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (cflag & CSTOPB) {
16570f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		lStop = UART_LCR_STOP;
1658441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - stop bits = 2", __func__);
16590f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	} else {
16600f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		lStop = 0x00;
1661441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - stop bits = 1", __func__);
16620f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
16630f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16640f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define LCR_BITS_MASK		0x03	/* Mask for bits/char field */
16650f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define LCR_STOP_MASK		0x04	/* Mask for stop bits field */
16660f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman#define LCR_PAR_MASK		0x38	/* Mask for parity field */
16670f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16680f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Update the LCR with the correct value */
16694da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	mos7720_port->shadowLCR &=
167063b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
16710f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port->shadowLCR |= (lData | lParity | lStop);
16720f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16740f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Disable Interrupts */
167563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, IER, 0x00);
167663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, FCR, 0x00);
167763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, FCR, 0xcf);
16780f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Send the updated LCR value to the mos7720 */
168063b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, LCR, mos7720_port->shadowLCR);
168163b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	mos7720_port->shadowMCR = 0x0b;
168263b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR);
16830f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16840f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* set up the MCR register and send it to the mos7720 */
16850f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port->shadowMCR = UART_MCR_OUT2;
16860f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (cflag & CBAUD)
16870f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		mos7720_port->shadowMCR |= (UART_MCR_DTR | UART_MCR_RTS);
16880f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
16890f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (cflag & CRTSCTS) {
16900f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		mos7720_port->shadowMCR |= (UART_MCR_XONANY);
16914da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		/* To set hardware flow control to the specified *
16924da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		 * serial port, in SP1/2_CONTROL_REG             */
169363b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		if (port->number)
169463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn			write_mos_reg(serial, dummy, SP_CONTROL_REG, 0x01);
169563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		else
169663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn			write_mos_reg(serial, dummy, SP_CONTROL_REG, 0x02);
169763b917678fe6d63e633462b5be5a309511bcf3caMike Dunn
169863b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	} else
16990f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		mos7720_port->shadowMCR &= ~(UART_MCR_XONANY);
17000f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
170163b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, MCR, mos7720_port->shadowMCR);
17020f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17030f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Determine divisor based on baud rate */
17040f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	baud = tty_get_baud_rate(tty);
17050f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (!baud) {
17060f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		/* pick a default, any default... */
17070f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		dbg("Picked default baud...");
17080f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		baud = 9600;
17090f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
17100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (baud >= 230400) {
17120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		set_higher_rates(mos7720_port, baud);
17130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		/* Enable Interrupts */
171463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		write_mos_reg(serial, port_number, IER, 0x0c);
17150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
17160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
17170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1718441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - baud rate = %d", __func__, baud);
17190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	status = send_cmd_write_baud_rate(mos7720_port, baud);
172065d063ab21feea8cf65d64fba50a5c4fa7bfd6beAlan Cox	/* FIXME: needs to write actual resulting baud back not just
172165d063ab21feea8cf65d64fba50a5c4fa7bfd6beAlan Cox	   blindly do so */
172265d063ab21feea8cf65d64fba50a5c4fa7bfd6beAlan Cox	if (cflag & CBAUD)
172365d063ab21feea8cf65d64fba50a5c4fa7bfd6beAlan Cox		tty_encode_baud_rate(tty, baud, baud);
17240f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* Enable Interrupts */
172563b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(serial, port_number, IER, 0x0c);
17260f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (port->read_urb->status != -EINPROGRESS) {
17280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
17290f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (status)
17300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			dbg("usb_submit_urb(read bulk) failed, status = %d",
17310f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			    status);
17320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
17330f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
17340f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17350f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
17360f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * mos7720_set_termios
17370f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	this function is called by the tty driver when it wants to change the
17380f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *	termios structure.
17390f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman */
174095da310e66ee8090119596c70ca8432e57f9a97fAlan Coxstatic void mos7720_set_termios(struct tty_struct *tty,
174195da310e66ee8090119596c70ca8432e57f9a97fAlan Cox		struct usb_serial_port *port, struct ktermios *old_termios)
17420f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
17430f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int status;
17440f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	unsigned int cflag;
17450f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial *serial;
17460f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port;
17470f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17480f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	serial = port->serial;
17490f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17500f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port = usb_get_serial_port_data(port);
17510f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17520f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL)
17530f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
17540f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17550f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (!mos7720_port->open) {
1756441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - port not opened", __func__);
17570f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return;
17580f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
17590f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1760b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	dbg("%s\n", "setting termios - ASPIRE");
17610f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17620f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	cflag = tty->termios->c_cflag;
17630f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1764441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - cflag %08x iflag %08x", __func__,
17650f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	    tty->termios->c_cflag,
17660f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	    RELEVANT_IFLAG(tty->termios->c_iflag));
17670f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1768441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - old cflag %08x old iflag %08x", __func__,
176965d063ab21feea8cf65d64fba50a5c4fa7bfd6beAlan Cox	    old_termios->c_cflag,
177065d063ab21feea8cf65d64fba50a5c4fa7bfd6beAlan Cox	    RELEVANT_IFLAG(old_termios->c_iflag));
17710f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1772441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port %d", __func__, port->number);
17730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17740f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* change the port settings to the new ones specified */
177595da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	change_port_settings(tty, mos7720_port, old_termios);
17760f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17774da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	if (port->read_urb->status != -EINPROGRESS) {
17780f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
17790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (status)
17800f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			dbg("usb_submit_urb(read bulk) failed, status = %d",
17810f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			    status);
17820f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
17830f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
17840f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
17850f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman/*
17860f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * get_lsr_info - get line status register info
17870f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman *
17880f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * Purpose: Let user call ioctl() to get info when the UART physically
17890f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * 	    is emptied.  On bus types like RS485, the transmitter must
17900f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * 	    release the bus after transmitting. This must be done when
17910f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * 	    the transmit shift register is empty, not be done when the
17920f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * 	    transmit holding register is empty.  This functionality
17930f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman * 	    allows an RS485 driver to be written in user space.
17940f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman */
17954da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Coxstatic int get_lsr_info(struct tty_struct *tty,
17964da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		struct moschip_port *mos7720_port, unsigned int __user *value)
17970f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
17982f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers	struct usb_serial_port *port = tty->driver_data;
17990f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	unsigned int result = 0;
18002f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers	unsigned char data = 0;
18012f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers	int port_number = port->number - port->serial->minor;
18022f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers	int count;
18030f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
180495da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	count = mos7720_chars_in_buffer(tty);
18050f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (count == 0) {
180663b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		read_mos_reg(port->serial, port_number, LSR, &data);
18072f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers		if ((data & (UART_LSR_TEMT | UART_LSR_THRE))
18082f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers					== (UART_LSR_TEMT | UART_LSR_THRE)) {
18092f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers			dbg("%s -- Empty", __func__);
18102f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers			result = TIOCSER_TEMT;
18112f9ea55c98bd03265e1c3eb114718eb2974df4cbKees Schoenmakers		}
18120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
18130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (copy_to_user(value, &result, sizeof(int)))
18140f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -EFAULT;
18150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return 0;
18160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
18170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
181860b33c133ca0b7c0b6072c87234b63fee6e80558Alan Coxstatic int mos7720_tiocmget(struct tty_struct *tty)
18190f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers{
18200f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	struct usb_serial_port *port = tty->driver_data;
18210f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
18220f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	unsigned int result = 0;
18230f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	unsigned int mcr ;
18240f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	unsigned int msr ;
18250f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
18260f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	dbg("%s - port %d", __func__, port->number);
18270f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
18280f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	mcr = mos7720_port->shadowMCR;
18290f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	msr = mos7720_port->shadowMSR;
18300f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
18310f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	result = ((mcr & UART_MCR_DTR)  ? TIOCM_DTR : 0)   /* 0x002 */
18320f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	  | ((mcr & UART_MCR_RTS)   ? TIOCM_RTS : 0)   /* 0x004 */
18330f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	  | ((msr & UART_MSR_CTS)   ? TIOCM_CTS : 0)   /* 0x020 */
18340f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	  | ((msr & UART_MSR_DCD)   ? TIOCM_CAR : 0)   /* 0x040 */
18350f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	  | ((msr & UART_MSR_RI)    ? TIOCM_RI :  0)   /* 0x080 */
18360f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	  | ((msr & UART_MSR_DSR)   ? TIOCM_DSR : 0);  /* 0x100 */
18370f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
18380f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	dbg("%s -- %x", __func__, result);
18390f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
18400f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	return result;
18410f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers}
18420f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
184320b9d17715017ae4dd4ec87fabc36d33b9de708eAlan Coxstatic int mos7720_tiocmset(struct tty_struct *tty,
184463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn			    unsigned int set, unsigned int clear)
18450f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers{
18460f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	struct usb_serial_port *port = tty->driver_data;
18470f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
18480f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	unsigned int mcr ;
18490f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	dbg("%s - port %d", __func__, port->number);
185060b33c133ca0b7c0b6072c87234b63fee6e80558Alan Cox	dbg("he was at tiocmset");
18510f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
18520f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	mcr = mos7720_port->shadowMCR;
18530f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
18540f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	if (set & TIOCM_RTS)
18550f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers		mcr |= UART_MCR_RTS;
18560f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	if (set & TIOCM_DTR)
18570f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers		mcr |= UART_MCR_DTR;
18580f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	if (set & TIOCM_LOOP)
18590f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers		mcr |= UART_MCR_LOOP;
18600f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
18610f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	if (clear & TIOCM_RTS)
18620f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers		mcr &= ~UART_MCR_RTS;
18630f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	if (clear & TIOCM_DTR)
18640f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers		mcr &= ~UART_MCR_DTR;
18650f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	if (clear & TIOCM_LOOP)
18660f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers		mcr &= ~UART_MCR_LOOP;
18670f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
18680f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	mos7720_port->shadowMCR = mcr;
186963b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(port->serial, port->number - port->serial->minor,
187063b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		      MCR, mos7720_port->shadowMCR);
18710f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
18720f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	return 0;
18730f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers}
18740f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers
18750bca1b913affbd7e2fdaffee62a499659a466eb5Alan Coxstatic int mos7720_get_icount(struct tty_struct *tty,
18760bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox				struct serial_icounter_struct *icount)
18770bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox{
18780bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	struct usb_serial_port *port = tty->driver_data;
18790bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	struct moschip_port *mos7720_port;
18800bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	struct async_icount cnow;
18810bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox
18820bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	mos7720_port = usb_get_serial_port_data(port);
18830bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	cnow = mos7720_port->icount;
18840bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox
18850bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->cts = cnow.cts;
18860bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->dsr = cnow.dsr;
18870bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->rng = cnow.rng;
18880bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->dcd = cnow.dcd;
18890bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->rx = cnow.rx;
18900bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->tx = cnow.tx;
18910bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->frame = cnow.frame;
18920bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->overrun = cnow.overrun;
18930bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->parity = cnow.parity;
18940bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->brk = cnow.brk;
18950bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	icount->buf_overrun = cnow.buf_overrun;
18960bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox
18970bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __func__,
18980bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox		port->number, icount->rx, icount->tx);
18990bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	return 0;
19000bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox}
19010bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox
19020f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanstatic int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd,
19030f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			  unsigned int __user *value)
19040f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
19050bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	unsigned int mcr;
19060f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	unsigned int arg;
19070f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19080f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_serial_port *port;
19090f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL)
19110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -1;
19120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19134da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	port = (struct usb_serial_port *)mos7720_port->port;
19140f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mcr = mos7720_port->shadowMCR;
19150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (copy_from_user(&arg, value, sizeof(int)))
19170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -EFAULT;
19180f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	switch (cmd) {
19200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case TIOCMBIS:
19210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (arg & TIOCM_RTS)
19220f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			mcr |= UART_MCR_RTS;
19230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (arg & TIOCM_DTR)
19240f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			mcr |= UART_MCR_RTS;
19250f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (arg & TIOCM_LOOP)
19260f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			mcr |= UART_MCR_LOOP;
19270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		break;
19280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19290f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case TIOCMBIC:
19300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (arg & TIOCM_RTS)
19310f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			mcr &= ~UART_MCR_RTS;
19320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (arg & TIOCM_DTR)
19330f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			mcr &= ~UART_MCR_RTS;
19340f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (arg & TIOCM_LOOP)
19350f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			mcr &= ~UART_MCR_LOOP;
19360f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		break;
19370f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19380f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
19390f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19400f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port->shadowMCR = mcr;
194163b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	write_mos_reg(port->serial, port->number - port->serial->minor,
194263b917678fe6d63e633462b5be5a309511bcf3caMike Dunn		      MCR, mos7720_port->shadowMCR);
19430f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19440f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return 0;
19450f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
19460f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19470f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanstatic int get_serial_info(struct moschip_port *mos7720_port,
19480f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			   struct serial_struct __user *retinfo)
19490f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
19500f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct serial_struct tmp;
19510f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19520f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (!retinfo)
19530f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -EFAULT;
19540f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19550f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	memset(&tmp, 0, sizeof(tmp));
19560f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19570f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	tmp.type		= PORT_16550A;
19580f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	tmp.line		= mos7720_port->port->serial->minor;
19590f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	tmp.port		= mos7720_port->port->number;
19600f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	tmp.irq			= 0;
19610f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	tmp.flags		= ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
19624da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	tmp.xmit_fifo_size	= NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
19630f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	tmp.baud_base		= 9600;
19640f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	tmp.close_delay		= 5*HZ;
19650f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	tmp.closing_wait	= 30*HZ;
19660f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19670f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
19680f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -EFAULT;
19690f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return 0;
19700f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
19710f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
197200a0d0d65b61241a718d0aee96f46b9a2d93bf26Alan Coxstatic int mos7720_ioctl(struct tty_struct *tty,
19730f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			 unsigned int cmd, unsigned long arg)
19740f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
197595da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	struct usb_serial_port *port = tty->driver_data;
19760f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port;
19770f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct async_icount cnow;
19780f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct async_icount cprev;
19790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19800f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	mos7720_port = usb_get_serial_port_data(port);
19810f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (mos7720_port == NULL)
19820f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -ENODEV;
19830f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
1984441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
19850f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
19860f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	switch (cmd) {
19870f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case TIOCSERGETLSR:
1988441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s (%d) TIOCSERGETLSR", __func__,  port->number);
19894da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		return get_lsr_info(tty, mos7720_port,
19904da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox					(unsigned int __user *)arg);
19910f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
199295da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	/* FIXME: These should be using the mode methods */
19930f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case TIOCMBIS:
19940f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case TIOCMBIC:
19954da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET",
19964da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox					__func__, port->number);
19970f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return set_modem_info(mos7720_port, cmd,
19980f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman				      (unsigned int __user *)arg);
19990f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
20000f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case TIOCGSERIAL:
2001441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s (%d) TIOCGSERIAL", __func__,  port->number);
20020f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return get_serial_info(mos7720_port,
20030f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman				       (struct serial_struct __user *)arg);
20040f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
20050f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	case TIOCMIWAIT:
2006441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s (%d) TIOCMIWAIT", __func__,  port->number);
20070f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		cprev = mos7720_port->icount;
20080f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		while (1) {
20090f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			if (signal_pending(current))
20100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman				return -ERESTARTSYS;
20110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			cnow = mos7720_port->icount;
20120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
20130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
20140f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman				return -EIO; /* no change => error */
20150f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
20160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
20170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
20184da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox			    ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
20190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman				return 0;
20200f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			}
20210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			cprev = cnow;
20220f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		}
20230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		/* NOTREACHED */
20240f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		break;
20250f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
20260f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
20270f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return -ENOIOCTLCMD;
20280f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
20290f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
20300f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanstatic int mos7720_startup(struct usb_serial *serial)
20310f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
20320f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct moschip_port *mos7720_port;
20330f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	struct usb_device *dev;
20340f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int i;
20350f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	char data;
203691f58ae61913b40da35e119017e70b3420c6f3a0Huzaifa Sidhpurwala	u16 product;
2037b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	int ret_val;
20380f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
2039441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s: Entering ..........", __func__);
20400f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
20410f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	if (!serial) {
20420f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		dbg("Invalid Handler");
20430f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		return -ENODEV;
20440f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
20450f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
204691f58ae61913b40da35e119017e70b3420c6f3a0Huzaifa Sidhpurwala	product = le16_to_cpu(serial->dev->descriptor.idProduct);
20470f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	dev = serial->dev;
20480f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
2049fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	/*
2050fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * The 7715 uses the first bulk in/out endpoint pair for the parallel
2051fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * port, and the second for the serial port.  Because the usbserial core
2052fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * assumes both pairs are serial ports, we must engage in a bit of
2053fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * subterfuge and swap the pointers for ports 0 and 1 in order to make
2054fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * port 0 point to the serial port.  However, both moschip devices use a
2055fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * single interrupt-in endpoint for both ports (as mentioned a little
2056fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * further down), and this endpoint was assigned to port 0.  So after
2057fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * the swap, we must copy the interrupt endpoint elements from port 1
2058fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 * (as newly assigned) to port 0, and null out port 1 pointers.
2059fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	 */
2060fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	if (product == MOSCHIP_DEVICE_ID_7715) {
2061fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		struct usb_serial_port *tmp = serial->port[0];
2062fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		serial->port[0] = serial->port[1];
2063fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		serial->port[1] = tmp;
2064fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		serial->port[0]->interrupt_in_urb = tmp->interrupt_in_urb;
2065fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		serial->port[0]->interrupt_in_buffer = tmp->interrupt_in_buffer;
2066fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		serial->port[0]->interrupt_in_endpointAddress =
2067fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn			tmp->interrupt_in_endpointAddress;
2068fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		serial->port[1]->interrupt_in_urb = NULL;
2069fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		serial->port[1]->interrupt_in_buffer = NULL;
2070fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	}
2071fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn
20720f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
2073b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* set up serial port private structures */
20740f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	for (i = 0; i < serial->num_ports; ++i) {
20750f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		mos7720_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
20760f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		if (mos7720_port == NULL) {
2077194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			dev_err(&dev->dev, "%s - Out of memory\n", __func__);
20780f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman			return -ENOMEM;
20790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		}
20800f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
20810f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		/* Initialize all port interrupt end point to port 0 int
20820f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		 * endpoint.  Our device has only one interrupt endpoint
2083fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn		 * common to all ports */
20844da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox		serial->port[i]->interrupt_in_endpointAddress =
20854da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox				serial->port[0]->interrupt_in_endpointAddress;
20860f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
20870f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		mos7720_port->port = serial->port[i];
20880f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		usb_set_serial_port_data(serial->port[i], mos7720_port);
20890f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
20900f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		dbg("port number is %d", serial->port[i]->number);
20910f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		dbg("serial number is %d", serial->minor);
20920f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	}
20930f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
20940f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
20950f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* setting configuration feature to one */
20960f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
20974da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox			(__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5*HZ);
20980f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
2099b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* start the interrupt urb */
2100b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
2101b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (ret_val)
2102b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		dev_err(&dev->dev,
2103b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			"%s - Error %d submitting control urb\n",
2104b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			__func__, ret_val);
2105b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
2106b69578df7e98659b7d94c905971a6d1025b431adMike Dunn#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
2107b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (product == MOSCHIP_DEVICE_ID_7715) {
2108b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		ret_val = mos7715_parport_init(serial);
2109b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		if (ret_val < 0)
2110b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			return ret_val;
2111b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
2112b69578df7e98659b7d94c905971a6d1025b431adMike Dunn#endif
21134da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	/* LSR For Port 1 */
211463b917678fe6d63e633462b5be5a309511bcf3caMike Dunn	read_mos_reg(serial, 0, LSR, &data);
21154da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan Cox	dbg("LSR:%x", data);
21160f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
21170f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	return 0;
21180f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
21190f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
2120f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Sternstatic void mos7720_release(struct usb_serial *serial)
21210f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman{
21220f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	int i;
21230f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
2124b69578df7e98659b7d94c905971a6d1025b431adMike Dunn#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
2125b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	/* close the parallel port */
2126b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
2127b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	if (le16_to_cpu(serial->dev->descriptor.idProduct)
2128b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	    == MOSCHIP_DEVICE_ID_7715) {
2129b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		struct urbtracker *urbtrack;
2130b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		unsigned long flags;
2131b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		struct mos7715_parport *mos_parport =
2132b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			usb_get_serial_data(serial);
2133b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
2134b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		/* prevent NULL ptr dereference in port callbacks */
2135b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_lock(&release_lock);
2136b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		mos_parport->pp->private_data = NULL;
2137b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_unlock(&release_lock);
2138b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
2139b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		/* wait for synchronous usb calls to return */
2140b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		if (mos_parport->msg_pending)
2141b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			wait_for_completion_timeout(&mos_parport->syncmsg_compl,
2142b69578df7e98659b7d94c905971a6d1025b431adMike Dunn						    MOS_WDR_TIMEOUT);
2143b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
2144b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		parport_remove_port(mos_parport->pp);
2145b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		usb_set_serial_data(serial, NULL);
2146b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		mos_parport->serial = NULL;
2147b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
2148b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		/* if tasklet currently scheduled, wait for it to complete */
2149b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		tasklet_kill(&mos_parport->urb_tasklet);
2150b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
2151b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		/* unlink any urbs sent by the tasklet  */
2152b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_lock_irqsave(&mos_parport->listlock, flags);
2153b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		list_for_each_entry(urbtrack,
2154b69578df7e98659b7d94c905971a6d1025b431adMike Dunn				    &mos_parport->active_urbs,
2155b69578df7e98659b7d94c905971a6d1025b431adMike Dunn				    urblist_entry)
2156b69578df7e98659b7d94c905971a6d1025b431adMike Dunn			usb_unlink_urb(urbtrack->urb);
2157b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		spin_unlock_irqrestore(&mos_parport->listlock, flags);
2158b69578df7e98659b7d94c905971a6d1025b431adMike Dunn
2159b69578df7e98659b7d94c905971a6d1025b431adMike Dunn		kref_put(&mos_parport->ref_count, destroy_mos_parport);
2160b69578df7e98659b7d94c905971a6d1025b431adMike Dunn	}
2161b69578df7e98659b7d94c905971a6d1025b431adMike Dunn#endif
21620f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	/* free private structure allocated for serial port */
2163f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	for (i = 0; i < serial->num_ports; ++i)
21640f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		kfree(usb_get_serial_port_data(serial->port[i]));
21650f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman}
21660f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
2167d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzlstatic struct usb_driver usb_driver = {
2168d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzl	.name =		"moschip7720",
2169d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzl	.probe =	usb_serial_probe,
2170d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzl	.disconnect =	usb_serial_disconnect,
2171d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzl	.id_table =	moschip_port_id_table,
2172d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzl};
2173d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzl
21740f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanstatic struct usb_serial_driver moschip7720_2port_driver = {
21750f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.driver = {
21760f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		.owner =	THIS_MODULE,
21770f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman		.name =		"moschip7720",
21780f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	},
21790f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.description		= "Moschip 2 port adapter",
21800f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.id_table		= moschip_port_id_table,
2181fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	.calc_num_ports		= mos77xx_calc_num_ports,
21820f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.open			= mos7720_open,
21830f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.close			= mos7720_close,
21840f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.throttle		= mos7720_throttle,
21850f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.unthrottle		= mos7720_unthrottle,
2186fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	.probe			= mos77xx_probe,
21870f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.attach			= mos7720_startup,
2188f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	.release		= mos7720_release,
21890f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.ioctl			= mos7720_ioctl,
21900f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	.tiocmget		= mos7720_tiocmget,
21910f608f8926968b4beee2cb00ef05522ad84f36ebKees Schoenmakers	.tiocmset		= mos7720_tiocmset,
21920bca1b913affbd7e2fdaffee62a499659a466eb5Alan Cox	.get_icount		= mos7720_get_icount,
21930f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.set_termios		= mos7720_set_termios,
21940f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.write			= mos7720_write,
21950f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.write_room		= mos7720_write_room,
21960f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.chars_in_buffer	= mos7720_chars_in_buffer,
21970f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.break_ctl		= mos7720_break,
21980f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman	.read_bulk_callback	= mos7720_bulk_in_callback,
2199fb088e335d78f866be2e56eac6d500112a96aa11Mike Dunn	.read_int_callback	= NULL  /* dynamically assigned in probe() */
22000f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman};
22010f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
22024d2a7aff1062048ea59214b49ca1d915586d6d6dAlan Sternstatic struct usb_serial_driver * const serial_drivers[] = {
22034d2a7aff1062048ea59214b49ca1d915586d6d6dAlan Stern	&moschip7720_2port_driver, NULL
22044d2a7aff1062048ea59214b49ca1d915586d6d6dAlan Stern};
22054d2a7aff1062048ea59214b49ca1d915586d6d6dAlan Stern
2206964e2b8425f5f37baa4f1fe6c15cdaf258d5cb51Greg Kroah-Hartmanmodule_usb_serial_driver(usb_driver, serial_drivers);
22070f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
22084da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan CoxMODULE_AUTHOR(DRIVER_AUTHOR);
22094da1a17dbc5211ac90264cfc4f0e076c8bdc1732Alan CoxMODULE_DESCRIPTION(DRIVER_DESC);
22100f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-HartmanMODULE_LICENSE("GPL");
22110f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartman
22120f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-Hartmanmodule_param(debug, bool, S_IRUGO | S_IWUSR);
22130f64478cbc7a008fe7b7e9ae79a73d8a6904ead8Greg Kroah-HartmanMODULE_PARM_DESC(debug, "Debug enabled or not");
2214