xref: /openbmc/linux/drivers/usb/serial/pl2303.c (revision 07588a58ef6d744638940c030619edd46a35b87a)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Prolific PL2303 USB to serial adaptor driver
41da177e4SLinus Torvalds  *
54d0dce3eSGreg Kroah-Hartman  * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com)
61da177e4SLinus Torvalds  * Copyright (C) 2003 IBM Corp.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * Original driver for 2.2.x by anonymous
91da177e4SLinus Torvalds  *
10ecefae6dSMauro Carvalho Chehab  * See Documentation/usb/usb-serial.rst for more information on using this
113a0f43e9SAlan Cox  * driver
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/errno.h>
161da177e4SLinus Torvalds #include <linux/slab.h>
171da177e4SLinus Torvalds #include <linux/tty.h>
181da177e4SLinus Torvalds #include <linux/tty_driver.h>
191da177e4SLinus Torvalds #include <linux/tty_flip.h>
201da177e4SLinus Torvalds #include <linux/serial.h>
211da177e4SLinus Torvalds #include <linux/module.h>
221da177e4SLinus Torvalds #include <linux/moduleparam.h>
231da177e4SLinus Torvalds #include <linux/spinlock.h>
243a0f43e9SAlan Cox #include <linux/uaccess.h>
251da177e4SLinus Torvalds #include <linux/usb.h>
26a969888cSGreg Kroah-Hartman #include <linux/usb/serial.h>
27b2d6d98fSJohan Hovold #include <asm/unaligned.h>
281da177e4SLinus Torvalds #include "pl2303.h"
291da177e4SLinus Torvalds 
30228e4105SJohan Hovold 
31228e4105SJohan Hovold #define PL2303_QUIRK_UART_STATE_IDX0		BIT(0)
3223c6acb9SJohan Hovold #define PL2303_QUIRK_LEGACY			BIT(1)
339d717271SJohan Hovold #define PL2303_QUIRK_ENDPOINT_HACK		BIT(2)
34228e4105SJohan Hovold 
357d40d7e8SNémeth Márton static const struct usb_device_id id_table[] = {
369d717271SJohan Hovold 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID),
379d717271SJohan Hovold 		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
381da177e4SLinus Torvalds 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
393d861494SPeter Moulder 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) },
401da177e4SLinus Torvalds 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) },
41d08dd3f3SGreg Kroah-Hartman 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_CHILITAG) },
421da177e4SLinus Torvalds 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) },
43b483b6aaSMax Arnold 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) },
444be2fa18SSteve Murphy 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) },
45727df356SGreg Kroah-Hartman 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) },
4618344a1cSSimone Contini 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) },
4796a3e79eSDario Lombardo 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) },
4891fcb1ceSGreg KH 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) },
494dcf9ddcSCharles Yeh 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_TB) },
50ebd09f1cSCharles Yeh 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GC) },
51ebd09f1cSCharles Yeh 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GB) },
52ebd09f1cSCharles Yeh 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GT) },
53ebd09f1cSCharles Yeh 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GL) },
54ebd09f1cSCharles Yeh 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GE) },
55ebd09f1cSCharles Yeh 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GS) },
561da177e4SLinus Torvalds 	{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
578a28dea3SMasakazu Mokuno 	{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
589d717271SJohan Hovold 	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID),
599d717271SJohan Hovold 		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
603b6bcd3dSGreg Kroah-Hartman 	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_UC485),
613b6bcd3dSGreg Kroah-Hartman 		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
6229c692c9SMovie Song 	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_UC232B),
6329c692c9SMovie Song 		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
64d07830dbSMarcel J.E. Mol 	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) },
651da177e4SLinus Torvalds 	{ USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
661da177e4SLinus Torvalds 	{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
671da177e4SLinus Torvalds 	{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) },
681da177e4SLinus Torvalds 	{ USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) },
6958381719SWang Jun 	{ USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) },
701da177e4SLinus Torvalds 	{ USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) },
711da177e4SLinus Torvalds 	{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) },
721da177e4SLinus Torvalds 	{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
731da177e4SLinus Torvalds 	{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
741da177e4SLinus Torvalds 	{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
751da177e4SLinus Torvalds 	{ USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
761da177e4SLinus Torvalds 	{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) },
77228e4105SJohan Hovold 	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1),
78228e4105SJohan Hovold 		.driver_info = PL2303_QUIRK_UART_STATE_IDX0 },
79228e4105SJohan Hovold 	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65),
80228e4105SJohan Hovold 		.driver_info = PL2303_QUIRK_UART_STATE_IDX0 },
81228e4105SJohan Hovold 	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75),
82228e4105SJohan Hovold 		.driver_info = PL2303_QUIRK_UART_STATE_IDX0 },
839d717271SJohan Hovold 	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81),
849d717271SJohan Hovold 		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
85912299f6SAlan Cox 	{ USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_ID_S81) }, /* Benq/Siemens S81 */
86acbb36f1SPeter Favrholdt 	{ USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) },
8768e110a0SRobert Spanton 	{ USB_DEVICE(NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID) },
88838b4281SMartin Gingras 	{ USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) },
896cceb05fSDenis MONTERRAT 	{ USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) },
90c6c27721SChristian Lindner 	{ USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) },
91491b04ceSDick Streefland 	{ USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) },
923b928474SMatthew Meno 	{ USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) },
93b7aa94b6SKim Oldfield 	{ USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) },
949d717271SJohan Hovold 	{ USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID),
959d717271SJohan Hovold 		.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
962d94b981SYOSHIFUJI Hideaki 	{ USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) },
979e3285dbSMagnus Damm 	{ USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) },
98cc311ee7SDamien Stuart 	{ USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) },
997c992001SMatthew Arnold 	{ USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) },
100af4b8514SMike Provencher 	{ USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) },
1018d503f20SScott Chen 	{ USB_DEVICE(HP_VENDOR_ID, HP_LD220TA_PRODUCT_ID) },
102cecc113cSScott Chen 	{ USB_DEVICE(HP_VENDOR_ID, HP_LD381_PRODUCT_ID) },
103031f9664SScott Chen 	{ USB_DEVICE(HP_VENDOR_ID, HP_LD381GC_PRODUCT_ID) },
104b16c02fbSAaron Sanders 	{ USB_DEVICE(HP_VENDOR_ID, HP_LD960_PRODUCT_ID) },
1058d503f20SScott Chen 	{ USB_DEVICE(HP_VENDOR_ID, HP_LD960TA_PRODUCT_ID) },
106b16c02fbSAaron Sanders 	{ USB_DEVICE(HP_VENDOR_ID, HP_LCM220_PRODUCT_ID) },
107b16c02fbSAaron Sanders 	{ USB_DEVICE(HP_VENDOR_ID, HP_LCM960_PRODUCT_ID) },
1088d503f20SScott Chen 	{ USB_DEVICE(HP_VENDOR_ID, HP_LM920_PRODUCT_ID) },
10926a08f8bSScott Chen 	{ USB_DEVICE(HP_VENDOR_ID, HP_LM930_PRODUCT_ID) },
1108d503f20SScott Chen 	{ USB_DEVICE(HP_VENDOR_ID, HP_LM940_PRODUCT_ID) },
1118d503f20SScott Chen 	{ USB_DEVICE(HP_VENDOR_ID, HP_TD620_PRODUCT_ID) },
1128540d666SGianpaolo Cugola 	{ USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) },
113f36ecd5dSJef Driesen 	{ USB_DEVICE(ZEAGLE_VENDOR_ID, ZEAGLE_N2ITION3_PRODUCT_ID) },
11449276560SKhanh-Dang Nguyen Thu Lam 	{ USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) },
11535904e6bSPawel Ludwikow 	{ USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) },
1169a61d726SManuel Jander 	{ USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) },
117f8e8c1b2SZolton Jheng 	{ USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530GC_PRODUCT_ID) },
118598f0b70SEric Benoit 	{ USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) },
119c5f81656SChris Packham 	{ USB_DEVICE(AT_VENDOR_ID, AT_VTKIT3_PRODUCT_ID) },
120e1d15646SEddie James 	{ USB_DEVICE(IBM_VENDOR_ID, IBM_PRODUCT_ID) },
121*39d69238SJunhao Xie 	{ USB_DEVICE(MACROSILICON_VENDOR_ID, MACROSILICON_MS3020_PRODUCT_ID) },
1221da177e4SLinus Torvalds 	{ }					/* Terminating entry */
1231da177e4SLinus Torvalds };
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds MODULE_DEVICE_TABLE(usb, id_table);
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds #define SET_LINE_REQUEST_TYPE		0x21
1281da177e4SLinus Torvalds #define SET_LINE_REQUEST		0x20
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds #define SET_CONTROL_REQUEST_TYPE	0x21
1311da177e4SLinus Torvalds #define SET_CONTROL_REQUEST		0x22
1321da177e4SLinus Torvalds #define CONTROL_DTR			0x01
1331da177e4SLinus Torvalds #define CONTROL_RTS			0x02
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds #define BREAK_REQUEST_TYPE		0x21
1361da177e4SLinus Torvalds #define BREAK_REQUEST			0x23
1371da177e4SLinus Torvalds #define BREAK_ON			0xffff
1381da177e4SLinus Torvalds #define BREAK_OFF			0x0000
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds #define GET_LINE_REQUEST_TYPE		0xa1
1411da177e4SLinus Torvalds #define GET_LINE_REQUEST		0x21
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds #define VENDOR_WRITE_REQUEST_TYPE	0x40
1441da177e4SLinus Torvalds #define VENDOR_WRITE_REQUEST		0x01
145ebd09f1cSCharles Yeh #define VENDOR_WRITE_NREQUEST		0x80
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds #define VENDOR_READ_REQUEST_TYPE	0xc0
1481da177e4SLinus Torvalds #define VENDOR_READ_REQUEST		0x01
149ebd09f1cSCharles Yeh #define VENDOR_READ_NREQUEST		0x81
1501da177e4SLinus Torvalds 
151228e4105SJohan Hovold #define UART_STATE_INDEX		8
152dbfd2866SJohan Hovold #define UART_STATE_MSR_MASK		0x8b
1531da177e4SLinus Torvalds #define UART_STATE_TRANSIENT_MASK	0x74
1541da177e4SLinus Torvalds #define UART_DCD			0x01
1551da177e4SLinus Torvalds #define UART_DSR			0x02
1561da177e4SLinus Torvalds #define UART_BREAK_ERROR		0x04
1571da177e4SLinus Torvalds #define UART_RING			0x08
1581da177e4SLinus Torvalds #define UART_FRAME_ERROR		0x10
1591da177e4SLinus Torvalds #define UART_PARITY_ERROR		0x20
1601da177e4SLinus Torvalds #define UART_OVERRUN_ERROR		0x40
1611da177e4SLinus Torvalds #define UART_CTS			0x80
1621da177e4SLinus Torvalds 
163f64c3ab2SJohan Hovold #define PL2303_FLOWCTRL_MASK		0xf0
164f64c3ab2SJohan Hovold 
165ebd09f1cSCharles Yeh #define PL2303_READ_TYPE_HX_STATUS	0x8080
166ebd09f1cSCharles Yeh 
167ebd09f1cSCharles Yeh #define PL2303_HXN_RESET_REG		0x07
168ebd09f1cSCharles Yeh #define PL2303_HXN_RESET_UPSTREAM_PIPE	0x02
169ebd09f1cSCharles Yeh #define PL2303_HXN_RESET_DOWNSTREAM_PIPE	0x01
170ebd09f1cSCharles Yeh 
171ebd09f1cSCharles Yeh #define PL2303_HXN_FLOWCTRL_REG		0x0a
172ebd09f1cSCharles Yeh #define PL2303_HXN_FLOWCTRL_MASK	0x1c
173ebd09f1cSCharles Yeh #define PL2303_HXN_FLOWCTRL_NONE	0x1c
174ebd09f1cSCharles Yeh #define PL2303_HXN_FLOWCTRL_RTS_CTS	0x18
175ebd09f1cSCharles Yeh #define PL2303_HXN_FLOWCTRL_XON_XOFF	0x0c
176ebd09f1cSCharles Yeh 
1776ff58ae1SJohan Hovold static int pl2303_set_break(struct usb_serial_port *port, bool enable);
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds enum pl2303_type {
180ca82f648SJohan Hovold 	TYPE_H,
1818a7bf751SJohan Hovold 	TYPE_HX,
1828a7bf751SJohan Hovold 	TYPE_TA,
1838a7bf751SJohan Hovold 	TYPE_TB,
1848a7bf751SJohan Hovold 	TYPE_HXD,
1858a7bf751SJohan Hovold 	TYPE_HXN,
186359defdaSJohan Hovold 	TYPE_COUNT
187359defdaSJohan Hovold };
188359defdaSJohan Hovold 
189359defdaSJohan Hovold struct pl2303_type_data {
1908cbc7539SJohan Hovold 	const char *name;
191359defdaSJohan Hovold 	speed_t max_baud_rate;
192359defdaSJohan Hovold 	unsigned long quirks;
19368270dabSJohan Hovold 	unsigned int no_autoxonxoff:1;
194979d9cbeSJohan Hovold 	unsigned int no_divisors:1;
195764de105SMichael G. Katzmann 	unsigned int alt_divisors:1;
1961da177e4SLinus Torvalds };
1971da177e4SLinus Torvalds 
1988bf769ebSJohan Hovold struct pl2303_serial_private {
199b6934681SJohan Hovold 	const struct pl2303_type_data *type;
200228e4105SJohan Hovold 	unsigned long quirks;
2018bf769ebSJohan Hovold };
2028bf769ebSJohan Hovold 
2031da177e4SLinus Torvalds struct pl2303_private {
2041da177e4SLinus Torvalds 	spinlock_t lock;
2051da177e4SLinus Torvalds 	u8 line_control;
2061da177e4SLinus Torvalds 	u8 line_status;
207623c8263SJohan Hovold 
208623c8263SJohan Hovold 	u8 line_settings[7];
2091da177e4SLinus Torvalds };
2101da177e4SLinus Torvalds 
211b6934681SJohan Hovold static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
212ca82f648SJohan Hovold 	[TYPE_H] = {
2138cbc7539SJohan Hovold 		.name			= "H",
214359defdaSJohan Hovold 		.max_baud_rate		= 1228800,
215359defdaSJohan Hovold 		.quirks			= PL2303_QUIRK_LEGACY,
21668270dabSJohan Hovold 		.no_autoxonxoff		= true,
217359defdaSJohan Hovold 	},
218399aa9a7SLauri Hintsala 	[TYPE_HX] = {
2198cbc7539SJohan Hovold 		.name			= "HX",
2208a7bf751SJohan Hovold 		.max_baud_rate		= 6000000,
2218a7bf751SJohan Hovold 	},
2228a7bf751SJohan Hovold 	[TYPE_TA] = {
2238cbc7539SJohan Hovold 		.name			= "TA",
2248a7bf751SJohan Hovold 		.max_baud_rate		= 6000000,
225764de105SMichael G. Katzmann 		.alt_divisors		= true,
2268a7bf751SJohan Hovold 	},
2278a7bf751SJohan Hovold 	[TYPE_TB] = {
2288cbc7539SJohan Hovold 		.name			= "TB",
2298a7bf751SJohan Hovold 		.max_baud_rate		= 12000000,
230764de105SMichael G. Katzmann 		.alt_divisors		= true,
2318a7bf751SJohan Hovold 	},
2328a7bf751SJohan Hovold 	[TYPE_HXD] = {
2338cbc7539SJohan Hovold 		.name			= "HXD",
234399aa9a7SLauri Hintsala 		.max_baud_rate		= 12000000,
235399aa9a7SLauri Hintsala 	},
236ebd09f1cSCharles Yeh 	[TYPE_HXN] = {
2378cbc7539SJohan Hovold 		.name			= "G",
238ebd09f1cSCharles Yeh 		.max_baud_rate		= 12000000,
239979d9cbeSJohan Hovold 		.no_divisors		= true,
240ebd09f1cSCharles Yeh 	},
241359defdaSJohan Hovold };
242359defdaSJohan Hovold 
pl2303_vendor_read(struct usb_serial * serial,u16 value,unsigned char buf[1])243362eb026SJohan Hovold static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
244362eb026SJohan Hovold 							unsigned char buf[1])
245eb44da0bSSarah Sharp {
246ebd09f1cSCharles Yeh 	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
247362eb026SJohan Hovold 	struct device *dev = &serial->interface->dev;
248ebd09f1cSCharles Yeh 	u8 request;
249ccfe8188SJohan Hovold 	int res;
250ccfe8188SJohan Hovold 
251ebd09f1cSCharles Yeh 	if (spriv->type == &pl2303_type_data[TYPE_HXN])
252ebd09f1cSCharles Yeh 		request = VENDOR_READ_NREQUEST;
253ebd09f1cSCharles Yeh 	else
254ebd09f1cSCharles Yeh 		request = VENDOR_READ_REQUEST;
255ebd09f1cSCharles Yeh 
256ccfe8188SJohan Hovold 	res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
257ebd09f1cSCharles Yeh 			request, VENDOR_READ_REQUEST_TYPE,
258362eb026SJohan Hovold 			value, 0, buf, 1, 100);
259362eb026SJohan Hovold 	if (res != 1) {
260362eb026SJohan Hovold 		dev_err(dev, "%s - failed to read [%04x]: %d\n", __func__,
261362eb026SJohan Hovold 								value, res);
262362eb026SJohan Hovold 		if (res >= 0)
263362eb026SJohan Hovold 			res = -EIO;
264ccfe8188SJohan Hovold 
265eb44da0bSSarah Sharp 		return res;
266eb44da0bSSarah Sharp 	}
267eb44da0bSSarah Sharp 
268362eb026SJohan Hovold 	dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, buf[0]);
269362eb026SJohan Hovold 
270362eb026SJohan Hovold 	return 0;
271362eb026SJohan Hovold }
272362eb026SJohan Hovold 
pl2303_vendor_write(struct usb_serial * serial,u16 value,u16 index)273362eb026SJohan Hovold static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index)
274eb44da0bSSarah Sharp {
275ebd09f1cSCharles Yeh 	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
276362eb026SJohan Hovold 	struct device *dev = &serial->interface->dev;
277ebd09f1cSCharles Yeh 	u8 request;
278ccfe8188SJohan Hovold 	int res;
279ccfe8188SJohan Hovold 
280362eb026SJohan Hovold 	dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, index);
281362eb026SJohan Hovold 
282ebd09f1cSCharles Yeh 	if (spriv->type == &pl2303_type_data[TYPE_HXN])
283ebd09f1cSCharles Yeh 		request = VENDOR_WRITE_NREQUEST;
284ebd09f1cSCharles Yeh 	else
285ebd09f1cSCharles Yeh 		request = VENDOR_WRITE_REQUEST;
286ebd09f1cSCharles Yeh 
287ccfe8188SJohan Hovold 	res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
288ebd09f1cSCharles Yeh 			request, VENDOR_WRITE_REQUEST_TYPE,
289eb44da0bSSarah Sharp 			value, index, NULL, 0, 100);
290362eb026SJohan Hovold 	if (res) {
291362eb026SJohan Hovold 		dev_err(dev, "%s - failed to write [%04x]: %d\n", __func__,
292362eb026SJohan Hovold 								value, res);
293eb44da0bSSarah Sharp 		return res;
294eb44da0bSSarah Sharp 	}
295eb44da0bSSarah Sharp 
296362eb026SJohan Hovold 	return 0;
297362eb026SJohan Hovold }
298362eb026SJohan Hovold 
pl2303_update_reg(struct usb_serial * serial,u8 reg,u8 mask,u8 val)299f64c3ab2SJohan Hovold static int pl2303_update_reg(struct usb_serial *serial, u8 reg, u8 mask, u8 val)
300f64c3ab2SJohan Hovold {
301ebd09f1cSCharles Yeh 	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
302f64c3ab2SJohan Hovold 	int ret = 0;
303f64c3ab2SJohan Hovold 	u8 *buf;
304f64c3ab2SJohan Hovold 
305f64c3ab2SJohan Hovold 	buf = kmalloc(1, GFP_KERNEL);
306f64c3ab2SJohan Hovold 	if (!buf)
307f64c3ab2SJohan Hovold 		return -ENOMEM;
308f64c3ab2SJohan Hovold 
309ebd09f1cSCharles Yeh 	if (spriv->type == &pl2303_type_data[TYPE_HXN])
310ebd09f1cSCharles Yeh 		ret = pl2303_vendor_read(serial, reg, buf);
311ebd09f1cSCharles Yeh 	else
312f64c3ab2SJohan Hovold 		ret = pl2303_vendor_read(serial, reg | 0x80, buf);
313ebd09f1cSCharles Yeh 
314f64c3ab2SJohan Hovold 	if (ret)
315f64c3ab2SJohan Hovold 		goto out_free;
316f64c3ab2SJohan Hovold 
317f64c3ab2SJohan Hovold 	*buf &= ~mask;
318f64c3ab2SJohan Hovold 	*buf |= val & mask;
319f64c3ab2SJohan Hovold 
320f64c3ab2SJohan Hovold 	ret = pl2303_vendor_write(serial, reg, *buf);
321f64c3ab2SJohan Hovold out_free:
322f64c3ab2SJohan Hovold 	kfree(buf);
323f64c3ab2SJohan Hovold 
324f64c3ab2SJohan Hovold 	return ret;
325f64c3ab2SJohan Hovold }
326f64c3ab2SJohan Hovold 
pl2303_probe(struct usb_serial * serial,const struct usb_device_id * id)327228e4105SJohan Hovold static int pl2303_probe(struct usb_serial *serial,
328228e4105SJohan Hovold 					const struct usb_device_id *id)
329228e4105SJohan Hovold {
330228e4105SJohan Hovold 	usb_set_serial_data(serial, (void *)id->driver_info);
331228e4105SJohan Hovold 
332228e4105SJohan Hovold 	return 0;
333228e4105SJohan Hovold }
334228e4105SJohan Hovold 
3359d717271SJohan Hovold /*
3369d717271SJohan Hovold  * Use interrupt endpoint from first interface if available.
3379d717271SJohan Hovold  *
3389d717271SJohan Hovold  * This is needed due to the looney way its endpoints are set up.
3399d717271SJohan Hovold  */
pl2303_endpoint_hack(struct usb_serial * serial,struct usb_serial_endpoints * epds)3409d717271SJohan Hovold static int pl2303_endpoint_hack(struct usb_serial *serial,
3419fda620aSJohan Hovold 					struct usb_serial_endpoints *epds)
3429fda620aSJohan Hovold {
3439fda620aSJohan Hovold 	struct usb_interface *interface = serial->interface;
3449fda620aSJohan Hovold 	struct usb_device *dev = serial->dev;
3459fda620aSJohan Hovold 	struct device *ddev = &interface->dev;
3469fda620aSJohan Hovold 	struct usb_host_interface *iface_desc;
3479fda620aSJohan Hovold 	struct usb_endpoint_descriptor *endpoint;
3489fda620aSJohan Hovold 	unsigned int i;
3499fda620aSJohan Hovold 
3509d717271SJohan Hovold 	if (interface == dev->actconfig->interface[0])
3519d717271SJohan Hovold 		return 0;
3529d717271SJohan Hovold 
3539fda620aSJohan Hovold 	/* check out the endpoints of the other interface */
3549fda620aSJohan Hovold 	iface_desc = dev->actconfig->interface[0]->cur_altsetting;
3559d717271SJohan Hovold 
3569fda620aSJohan Hovold 	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
3579fda620aSJohan Hovold 		endpoint = &iface_desc->endpoint[i].desc;
3589d717271SJohan Hovold 
3599d717271SJohan Hovold 		if (!usb_endpoint_is_int_in(endpoint))
3609d717271SJohan Hovold 			continue;
3619d717271SJohan Hovold 
3629d717271SJohan Hovold 		dev_dbg(ddev, "found interrupt in on separate interface\n");
3639fda620aSJohan Hovold 		if (epds->num_interrupt_in < ARRAY_SIZE(epds->interrupt_in))
3649fda620aSJohan Hovold 			epds->interrupt_in[epds->num_interrupt_in++] = endpoint;
3659fda620aSJohan Hovold 	}
3669d717271SJohan Hovold 
3679d717271SJohan Hovold 	return 0;
3689fda620aSJohan Hovold }
3699fda620aSJohan Hovold 
pl2303_calc_num_ports(struct usb_serial * serial,struct usb_serial_endpoints * epds)3709d717271SJohan Hovold static int pl2303_calc_num_ports(struct usb_serial *serial,
3719d717271SJohan Hovold 					struct usb_serial_endpoints *epds)
3729d717271SJohan Hovold {
3739d717271SJohan Hovold 	unsigned long quirks = (unsigned long)usb_get_serial_data(serial);
3749d717271SJohan Hovold 	struct device *dev = &serial->interface->dev;
3759d717271SJohan Hovold 	int ret;
3769d717271SJohan Hovold 
3779d717271SJohan Hovold 	if (quirks & PL2303_QUIRK_ENDPOINT_HACK) {
3789d717271SJohan Hovold 		ret = pl2303_endpoint_hack(serial, epds);
3799d717271SJohan Hovold 		if (ret)
3809d717271SJohan Hovold 			return ret;
3819fda620aSJohan Hovold 	}
3829fda620aSJohan Hovold 
3839fda620aSJohan Hovold 	if (epds->num_interrupt_in < 1) {
3849d717271SJohan Hovold 		dev_err(dev, "required interrupt-in endpoint missing\n");
3859fda620aSJohan Hovold 		return -ENODEV;
3869fda620aSJohan Hovold 	}
3879fda620aSJohan Hovold 
3889fda620aSJohan Hovold 	return 1;
3899fda620aSJohan Hovold }
3909fda620aSJohan Hovold 
pl2303_supports_hx_status(struct usb_serial * serial)391894758d0SJohan Hovold static bool pl2303_supports_hx_status(struct usb_serial *serial)
392894758d0SJohan Hovold {
393894758d0SJohan Hovold 	int ret;
394894758d0SJohan Hovold 	u8 buf;
395894758d0SJohan Hovold 
396894758d0SJohan Hovold 	ret = usb_control_msg_recv(serial->dev, 0, VENDOR_READ_REQUEST,
397894758d0SJohan Hovold 			VENDOR_READ_REQUEST_TYPE, PL2303_READ_TYPE_HX_STATUS,
398894758d0SJohan Hovold 			0, &buf, 1, 100, GFP_KERNEL);
399894758d0SJohan Hovold 
400894758d0SJohan Hovold 	return ret == 0;
401894758d0SJohan Hovold }
402894758d0SJohan Hovold 
pl2303_detect_type(struct usb_serial * serial)4038a7bf751SJohan Hovold static int pl2303_detect_type(struct usb_serial *serial)
404e5f48c81SJohan Hovold {
405e5f48c81SJohan Hovold 	struct usb_device_descriptor *desc = &serial->dev->descriptor;
4068a7bf751SJohan Hovold 	u16 bcdDevice, bcdUSB;
407e5f48c81SJohan Hovold 
408e5f48c81SJohan Hovold 	/*
409ca82f648SJohan Hovold 	 * Legacy PL2303H, variants 0 and 1 (difference unknown).
410e5f48c81SJohan Hovold 	 */
411e5f48c81SJohan Hovold 	if (desc->bDeviceClass == 0x02)
412ca82f648SJohan Hovold 		return TYPE_H;		/* variant 0 */
413e5f48c81SJohan Hovold 
414e5f48c81SJohan Hovold 	if (desc->bMaxPacketSize0 != 0x40) {
415e5f48c81SJohan Hovold 		if (desc->bDeviceClass == 0x00 || desc->bDeviceClass == 0xff)
416ca82f648SJohan Hovold 			return TYPE_H;	/* variant 1 */
417e5f48c81SJohan Hovold 
418ca82f648SJohan Hovold 		return TYPE_H;		/* variant 0 */
419e5f48c81SJohan Hovold 	}
420e5f48c81SJohan Hovold 
4218a7bf751SJohan Hovold 	bcdDevice = le16_to_cpu(desc->bcdDevice);
4228a7bf751SJohan Hovold 	bcdUSB = le16_to_cpu(desc->bcdUSB);
4238a7bf751SJohan Hovold 
4241e9faef4SJohan Hovold 	switch (bcdUSB) {
425e82e7c6dSJohan Hovold 	case 0x101:
426e82e7c6dSJohan Hovold 		/* USB 1.0.1? Let's assume they meant 1.1... */
427e82e7c6dSJohan Hovold 		fallthrough;
4281e9faef4SJohan Hovold 	case 0x110:
4298a7bf751SJohan Hovold 		switch (bcdDevice) {
4308a7bf751SJohan Hovold 		case 0x300:
431e5f48c81SJohan Hovold 			return TYPE_HX;
4328a7bf751SJohan Hovold 		case 0x400:
4338a7bf751SJohan Hovold 			return TYPE_HXD;
4341e9faef4SJohan Hovold 		default:
4351e9faef4SJohan Hovold 			return TYPE_HX;
4361e9faef4SJohan Hovold 		}
4371e9faef4SJohan Hovold 		break;
4381e9faef4SJohan Hovold 	case 0x200:
4391e9faef4SJohan Hovold 		switch (bcdDevice) {
440ae60aac5SJohan Hovold 		case 0x100:	/* GC */
441aa5721a9SJohan Hovold 		case 0x105:
4421e9faef4SJohan Hovold 			return TYPE_HXN;
443ae60aac5SJohan Hovold 		case 0x300:	/* GT / TA */
444ae60aac5SJohan Hovold 			if (pl2303_supports_hx_status(serial))
4451e9faef4SJohan Hovold 				return TYPE_TA;
446ae60aac5SJohan Hovold 			fallthrough;
447ae60aac5SJohan Hovold 		case 0x305:
448ae60aac5SJohan Hovold 		case 0x400:	/* GL */
449ae60aac5SJohan Hovold 		case 0x405:
450ae60aac5SJohan Hovold 			return TYPE_HXN;
451ae60aac5SJohan Hovold 		case 0x500:	/* GE / TB */
452ae60aac5SJohan Hovold 			if (pl2303_supports_hx_status(serial))
4538a7bf751SJohan Hovold 				return TYPE_TB;
454ae60aac5SJohan Hovold 			fallthrough;
455ae60aac5SJohan Hovold 		case 0x505:
456ae60aac5SJohan Hovold 		case 0x600:	/* GS */
457ae60aac5SJohan Hovold 		case 0x605:
458ae60aac5SJohan Hovold 		case 0x700:	/* GR */
459ae60aac5SJohan Hovold 		case 0x705:
460ae60aac5SJohan Hovold 			return TYPE_HXN;
4618a7bf751SJohan Hovold 		}
4621e9faef4SJohan Hovold 		break;
4631e9faef4SJohan Hovold 	}
4648a7bf751SJohan Hovold 
4658a7bf751SJohan Hovold 	dev_err(&serial->interface->dev,
4668a7bf751SJohan Hovold 			"unknown device type, please report to linux-usb@vger.kernel.org\n");
4678a7bf751SJohan Hovold 	return -ENODEV;
468e5f48c81SJohan Hovold }
469e5f48c81SJohan Hovold 
pl2303_startup(struct usb_serial * serial)4701da177e4SLinus Torvalds static int pl2303_startup(struct usb_serial *serial)
4711da177e4SLinus Torvalds {
4728bf769ebSJohan Hovold 	struct pl2303_serial_private *spriv;
473e5f48c81SJohan Hovold 	enum pl2303_type type;
4743e152505SSarah Sharp 	unsigned char *buf;
4758a7bf751SJohan Hovold 	int ret;
4768a7bf751SJohan Hovold 
4778a7bf751SJohan Hovold 	ret = pl2303_detect_type(serial);
4788a7bf751SJohan Hovold 	if (ret < 0)
4798a7bf751SJohan Hovold 		return ret;
4808a7bf751SJohan Hovold 
4818a7bf751SJohan Hovold 	type = ret;
4828cbc7539SJohan Hovold 	dev_dbg(&serial->interface->dev, "device type: %s\n", pl2303_type_data[type].name);
4838bf769ebSJohan Hovold 
4848bf769ebSJohan Hovold 	spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
4858bf769ebSJohan Hovold 	if (!spriv)
4868bf769ebSJohan Hovold 		return -ENOMEM;
4871da177e4SLinus Torvalds 
488359defdaSJohan Hovold 	spriv->type = &pl2303_type_data[type];
489228e4105SJohan Hovold 	spriv->quirks = (unsigned long)usb_get_serial_data(serial);
490359defdaSJohan Hovold 	spriv->quirks |= spriv->type->quirks;
491228e4105SJohan Hovold 
4928bf769ebSJohan Hovold 	usb_set_serial_data(serial, spriv);
4933e152505SSarah Sharp 
494ebd09f1cSCharles Yeh 	if (type != TYPE_HXN) {
495e5f48c81SJohan Hovold 		buf = kmalloc(1, GFP_KERNEL);
496e5f48c81SJohan Hovold 		if (!buf) {
497e5f48c81SJohan Hovold 			kfree(spriv);
498e5f48c81SJohan Hovold 			return -ENOMEM;
499e5f48c81SJohan Hovold 		}
500e5f48c81SJohan Hovold 
501362eb026SJohan Hovold 		pl2303_vendor_read(serial, 0x8484, buf);
502362eb026SJohan Hovold 		pl2303_vendor_write(serial, 0x0404, 0);
503362eb026SJohan Hovold 		pl2303_vendor_read(serial, 0x8484, buf);
504362eb026SJohan Hovold 		pl2303_vendor_read(serial, 0x8383, buf);
505362eb026SJohan Hovold 		pl2303_vendor_read(serial, 0x8484, buf);
506362eb026SJohan Hovold 		pl2303_vendor_write(serial, 0x0404, 1);
507362eb026SJohan Hovold 		pl2303_vendor_read(serial, 0x8484, buf);
508362eb026SJohan Hovold 		pl2303_vendor_read(serial, 0x8383, buf);
509362eb026SJohan Hovold 		pl2303_vendor_write(serial, 0, 1);
510362eb026SJohan Hovold 		pl2303_vendor_write(serial, 1, 0);
51123c6acb9SJohan Hovold 		if (spriv->quirks & PL2303_QUIRK_LEGACY)
512362eb026SJohan Hovold 			pl2303_vendor_write(serial, 2, 0x24);
5137f966ac7SJohan Hovold 		else
5147f966ac7SJohan Hovold 			pl2303_vendor_write(serial, 2, 0x44);
5153e152505SSarah Sharp 
5163e152505SSarah Sharp 		kfree(buf);
517e5f48c81SJohan Hovold 	}
518ccfe8188SJohan Hovold 
5191da177e4SLinus Torvalds 	return 0;
5201da177e4SLinus Torvalds }
5218bf769ebSJohan Hovold 
pl2303_release(struct usb_serial * serial)5228bf769ebSJohan Hovold static void pl2303_release(struct usb_serial *serial)
5238bf769ebSJohan Hovold {
524ccfe8188SJohan Hovold 	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
5258bf769ebSJohan Hovold 
5268bf769ebSJohan Hovold 	kfree(spriv);
5278bf769ebSJohan Hovold }
5288bf769ebSJohan Hovold 
pl2303_port_probe(struct usb_serial_port * port)5298bf769ebSJohan Hovold static int pl2303_port_probe(struct usb_serial_port *port)
5308bf769ebSJohan Hovold {
5318bf769ebSJohan Hovold 	struct pl2303_private *priv;
5328bf769ebSJohan Hovold 
5338bf769ebSJohan Hovold 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
5348bf769ebSJohan Hovold 	if (!priv)
5351da177e4SLinus Torvalds 		return -ENOMEM;
5368bf769ebSJohan Hovold 
5378bf769ebSJohan Hovold 	spin_lock_init(&priv->lock);
5388bf769ebSJohan Hovold 
5398bf769ebSJohan Hovold 	usb_set_serial_port_data(port, priv);
5408bf769ebSJohan Hovold 
541d7be6221SJohan Hovold 	port->port.drain_delay = 256;
542d7be6221SJohan Hovold 
5438bf769ebSJohan Hovold 	return 0;
5448bf769ebSJohan Hovold }
5458bf769ebSJohan Hovold 
pl2303_port_remove(struct usb_serial_port * port)546c5d1448fSUwe Kleine-König static void pl2303_port_remove(struct usb_serial_port *port)
5478bf769ebSJohan Hovold {
548ccfe8188SJohan Hovold 	struct pl2303_private *priv = usb_get_serial_port_data(port);
5498bf769ebSJohan Hovold 
5508bf769ebSJohan Hovold 	kfree(priv);
5511da177e4SLinus Torvalds }
5521da177e4SLinus Torvalds 
pl2303_set_control_lines(struct usb_serial_port * port,u8 value)553f45d0a5aSJohan Hovold static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value)
5541da177e4SLinus Torvalds {
555f45d0a5aSJohan Hovold 	struct usb_device *dev = port->serial->dev;
5561da177e4SLinus Torvalds 	int retval;
5571da177e4SLinus Torvalds 
558a6ec8245SJohan Hovold 	dev_dbg(&port->dev, "%s - %02x\n", __func__, value);
559a6ec8245SJohan Hovold 
5601da177e4SLinus Torvalds 	retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
5611da177e4SLinus Torvalds 				 SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
5621da177e4SLinus Torvalds 				 value, 0, NULL, 0, 100);
563a6ec8245SJohan Hovold 	if (retval)
564a6ec8245SJohan Hovold 		dev_err(&port->dev, "%s - failed: %d\n", __func__, retval);
565ccfe8188SJohan Hovold 
5661da177e4SLinus Torvalds 	return retval;
5671da177e4SLinus Torvalds }
5681da177e4SLinus Torvalds 
5697e12a6fcSGreg Kroah-Hartman /*
5705d85045fSJohan Hovold  * Returns the nearest supported baud rate that can be set directly without
5715d85045fSJohan Hovold  * using divisors.
5727e12a6fcSGreg Kroah-Hartman  */
pl2303_get_supported_baud_rate(speed_t baud)57359afe10eSJohan Hovold static speed_t pl2303_get_supported_baud_rate(speed_t baud)
57459afe10eSJohan Hovold {
57559afe10eSJohan Hovold 	static const speed_t baud_sup[] = {
57659afe10eSJohan Hovold 		75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600,
57759afe10eSJohan Hovold 		14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800,
5785d85045fSJohan Hovold 		614400, 921600, 1228800, 2457600, 3000000, 6000000
57959afe10eSJohan Hovold 	};
580692ed4ddSGreg Kroah-Hartman 
58159afe10eSJohan Hovold 	unsigned i;
58259afe10eSJohan Hovold 
583b2d6d98fSJohan Hovold 	for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) {
584b2d6d98fSJohan Hovold 		if (baud_sup[i] > baud)
58525b82868SFrank Schaefer 			break;
58625b82868SFrank Schaefer 	}
587692ed4ddSGreg Kroah-Hartman 
588b2d6d98fSJohan Hovold 	if (i == ARRAY_SIZE(baud_sup))
589b2d6d98fSJohan Hovold 		baud = baud_sup[i - 1];
5907e12a6fcSGreg Kroah-Hartman 	else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1]))
591b2d6d98fSJohan Hovold 		baud = baud_sup[i - 1];
592b2d6d98fSJohan Hovold 	else
593b2d6d98fSJohan Hovold 		baud = baud_sup[i];
594692ed4ddSGreg Kroah-Hartman 
59559afe10eSJohan Hovold 	return baud;
59659afe10eSJohan Hovold }
59759afe10eSJohan Hovold 
59820b4c787SJohan Hovold /*
59920b4c787SJohan Hovold  * NOTE: If unsupported baud rates are set directly, the PL2303 seems to
60020b4c787SJohan Hovold  *       use 9600 baud.
60120b4c787SJohan Hovold  */
pl2303_encode_baud_rate_direct(unsigned char buf[4],speed_t baud)60220b4c787SJohan Hovold static speed_t pl2303_encode_baud_rate_direct(unsigned char buf[4],
60320b4c787SJohan Hovold 								speed_t baud)
60420b4c787SJohan Hovold {
60520b4c787SJohan Hovold 	put_unaligned_le32(baud, buf);
60620b4c787SJohan Hovold 
60720b4c787SJohan Hovold 	return baud;
60820b4c787SJohan Hovold }
60920b4c787SJohan Hovold 
pl2303_encode_baud_rate_divisor(unsigned char buf[4],speed_t baud)610c82c6d45SJohan Hovold static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4],
611c82c6d45SJohan Hovold 								speed_t baud)
612c82c6d45SJohan Hovold {
61349bda212SMichał Pecio 	unsigned int baseline, mantissa, exponent;
614c82c6d45SJohan Hovold 
615c82c6d45SJohan Hovold 	/*
616c82c6d45SJohan Hovold 	 * Apparently the formula is:
61749bda212SMichał Pecio 	 *   baudrate = 12M * 32 / (mantissa * 4^exponent)
61849bda212SMichał Pecio 	 * where
61949bda212SMichał Pecio 	 *   mantissa = buf[8:0]
62049bda212SMichał Pecio 	 *   exponent = buf[11:9]
621c82c6d45SJohan Hovold 	 */
62249bda212SMichał Pecio 	baseline = 12000000 * 32;
62349bda212SMichał Pecio 	mantissa = baseline / baud;
62449bda212SMichał Pecio 	if (mantissa == 0)
62549bda212SMichał Pecio 		mantissa = 1;	/* Avoid dividing by zero if baud > 32*12M. */
62649bda212SMichał Pecio 	exponent = 0;
62749bda212SMichał Pecio 	while (mantissa >= 512) {
62849bda212SMichał Pecio 		if (exponent < 7) {
62949bda212SMichał Pecio 			mantissa >>= 2;	/* divide by 4 */
63049bda212SMichał Pecio 			exponent++;
63149bda212SMichał Pecio 		} else {
63249bda212SMichał Pecio 			/* Exponent is maxed. Trim mantissa and leave. */
63349bda212SMichał Pecio 			mantissa = 511;
63449bda212SMichał Pecio 			break;
63549bda212SMichał Pecio 		}
63649bda212SMichał Pecio 	}
63749bda212SMichał Pecio 
638c82c6d45SJohan Hovold 	buf[3] = 0x80;
639c82c6d45SJohan Hovold 	buf[2] = 0;
64049bda212SMichał Pecio 	buf[1] = exponent << 1 | mantissa >> 8;
64149bda212SMichał Pecio 	buf[0] = mantissa & 0xff;
64249bda212SMichał Pecio 
64349bda212SMichał Pecio 	/* Calculate and return the exact baud rate. */
64449bda212SMichał Pecio 	baud = (baseline / mantissa) >> (exponent << 1);
645c82c6d45SJohan Hovold 
646c82c6d45SJohan Hovold 	return baud;
647c82c6d45SJohan Hovold }
648c82c6d45SJohan Hovold 
pl2303_encode_baud_rate_divisor_alt(unsigned char buf[4],speed_t baud)649764de105SMichael G. Katzmann static speed_t pl2303_encode_baud_rate_divisor_alt(unsigned char buf[4],
650764de105SMichael G. Katzmann 								speed_t baud)
651764de105SMichael G. Katzmann {
652764de105SMichael G. Katzmann 	unsigned int baseline, mantissa, exponent;
653764de105SMichael G. Katzmann 
654764de105SMichael G. Katzmann 	/*
655764de105SMichael G. Katzmann 	 * Apparently, for the TA version the formula is:
656764de105SMichael G. Katzmann 	 *   baudrate = 12M * 32 / (mantissa * 2^exponent)
657764de105SMichael G. Katzmann 	 * where
658764de105SMichael G. Katzmann 	 *   mantissa = buf[10:0]
659764de105SMichael G. Katzmann 	 *   exponent = buf[15:13 16]
660764de105SMichael G. Katzmann 	 */
661764de105SMichael G. Katzmann 	baseline = 12000000 * 32;
662764de105SMichael G. Katzmann 	mantissa = baseline / baud;
663764de105SMichael G. Katzmann 	if (mantissa == 0)
664764de105SMichael G. Katzmann 		mantissa = 1;   /* Avoid dividing by zero if baud > 32*12M. */
665764de105SMichael G. Katzmann 	exponent = 0;
666764de105SMichael G. Katzmann 	while (mantissa >= 2048) {
667764de105SMichael G. Katzmann 		if (exponent < 15) {
668764de105SMichael G. Katzmann 			mantissa >>= 1; /* divide by 2 */
669764de105SMichael G. Katzmann 			exponent++;
670764de105SMichael G. Katzmann 		} else {
671764de105SMichael G. Katzmann 			/* Exponent is maxed. Trim mantissa and leave. */
672764de105SMichael G. Katzmann 			mantissa = 2047;
673764de105SMichael G. Katzmann 			break;
674764de105SMichael G. Katzmann 		}
675764de105SMichael G. Katzmann 	}
676764de105SMichael G. Katzmann 
677764de105SMichael G. Katzmann 	buf[3] = 0x80;
678764de105SMichael G. Katzmann 	buf[2] = exponent & 0x01;
679764de105SMichael G. Katzmann 	buf[1] = (exponent & ~0x01) << 4 | mantissa >> 8;
680764de105SMichael G. Katzmann 	buf[0] = mantissa & 0xff;
681764de105SMichael G. Katzmann 
682764de105SMichael G. Katzmann 	/* Calculate and return the exact baud rate. */
683764de105SMichael G. Katzmann 	baud = (baseline / mantissa) >> exponent;
684764de105SMichael G. Katzmann 
685764de105SMichael G. Katzmann 	return baud;
686764de105SMichael G. Katzmann }
687764de105SMichael G. Katzmann 
pl2303_encode_baud_rate(struct tty_struct * tty,struct usb_serial_port * port,u8 buf[4])68859afe10eSJohan Hovold static void pl2303_encode_baud_rate(struct tty_struct *tty,
68959afe10eSJohan Hovold 					struct usb_serial_port *port,
69059afe10eSJohan Hovold 					u8 buf[4])
69159afe10eSJohan Hovold {
69259afe10eSJohan Hovold 	struct usb_serial *serial = port->serial;
69359afe10eSJohan Hovold 	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
6945d85045fSJohan Hovold 	speed_t	baud_sup;
69559afe10eSJohan Hovold 	speed_t baud;
69659afe10eSJohan Hovold 
69759afe10eSJohan Hovold 	baud = tty_get_baud_rate(tty);
69859afe10eSJohan Hovold 	dev_dbg(&port->dev, "baud requested = %u\n", baud);
69959afe10eSJohan Hovold 	if (!baud)
70059afe10eSJohan Hovold 		return;
701871996edSJohan Hovold 
702871996edSJohan Hovold 	if (spriv->type->max_baud_rate)
703871996edSJohan Hovold 		baud = min_t(speed_t, baud, spriv->type->max_baud_rate);
7045d85045fSJohan Hovold 	/*
705399aa9a7SLauri Hintsala 	 * Use direct method for supported baud rates, otherwise use divisors.
706979d9cbeSJohan Hovold 	 * Newer chip types do not support divisor encoding.
7075d85045fSJohan Hovold 	 */
708979d9cbeSJohan Hovold 	if (spriv->type->no_divisors)
709979d9cbeSJohan Hovold 		baud_sup = baud;
710979d9cbeSJohan Hovold 	else
7115d85045fSJohan Hovold 		baud_sup = pl2303_get_supported_baud_rate(baud);
71220b4c787SJohan Hovold 
713399aa9a7SLauri Hintsala 	if (baud == baud_sup)
714399aa9a7SLauri Hintsala 		baud = pl2303_encode_baud_rate_direct(buf, baud);
715764de105SMichael G. Katzmann 	else if (spriv->type->alt_divisors)
716764de105SMichael G. Katzmann 		baud = pl2303_encode_baud_rate_divisor_alt(buf, baud);
7175d85045fSJohan Hovold 	else
718399aa9a7SLauri Hintsala 		baud = pl2303_encode_baud_rate_divisor(buf, baud);
7191da177e4SLinus Torvalds 
72015e7ceadSJohan Hovold 	/* Save resulting baud rate */
72115e7ceadSJohan Hovold 	tty_encode_baud_rate(tty, baud, baud);
722f84ee3b2SJohan Hovold 	dev_dbg(&port->dev, "baud set = %u\n", baud);
72315e7ceadSJohan Hovold }
72415e7ceadSJohan Hovold 
pl2303_get_line_request(struct usb_serial_port * port,unsigned char buf[7])725383d19c5SJohan Hovold static int pl2303_get_line_request(struct usb_serial_port *port,
726383d19c5SJohan Hovold 							unsigned char buf[7])
727383d19c5SJohan Hovold {
728383d19c5SJohan Hovold 	struct usb_device *udev = port->serial->dev;
729383d19c5SJohan Hovold 	int ret;
730383d19c5SJohan Hovold 
731383d19c5SJohan Hovold 	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
732383d19c5SJohan Hovold 				GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
733383d19c5SJohan Hovold 				0, 0, buf, 7, 100);
734383d19c5SJohan Hovold 	if (ret != 7) {
735383d19c5SJohan Hovold 		dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
736383d19c5SJohan Hovold 
737b5fda434SJohan Hovold 		if (ret >= 0)
738383d19c5SJohan Hovold 			ret = -EIO;
739383d19c5SJohan Hovold 
740383d19c5SJohan Hovold 		return ret;
741383d19c5SJohan Hovold 	}
742383d19c5SJohan Hovold 
743383d19c5SJohan Hovold 	dev_dbg(&port->dev, "%s - %7ph\n", __func__, buf);
744383d19c5SJohan Hovold 
745383d19c5SJohan Hovold 	return 0;
746383d19c5SJohan Hovold }
747383d19c5SJohan Hovold 
pl2303_set_line_request(struct usb_serial_port * port,unsigned char buf[7])748383d19c5SJohan Hovold static int pl2303_set_line_request(struct usb_serial_port *port,
749383d19c5SJohan Hovold 							unsigned char buf[7])
750383d19c5SJohan Hovold {
751383d19c5SJohan Hovold 	struct usb_device *udev = port->serial->dev;
752383d19c5SJohan Hovold 	int ret;
753383d19c5SJohan Hovold 
754383d19c5SJohan Hovold 	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
755383d19c5SJohan Hovold 				SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
756383d19c5SJohan Hovold 				0, 0, buf, 7, 100);
757b5fda434SJohan Hovold 	if (ret < 0) {
758383d19c5SJohan Hovold 		dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
759383d19c5SJohan Hovold 		return ret;
760383d19c5SJohan Hovold 	}
761383d19c5SJohan Hovold 
762383d19c5SJohan Hovold 	dev_dbg(&port->dev, "%s - %7ph\n", __func__, buf);
763383d19c5SJohan Hovold 
764383d19c5SJohan Hovold 	return 0;
765383d19c5SJohan Hovold }
766383d19c5SJohan Hovold 
pl2303_termios_change(const struct ktermios * a,const struct ktermios * b)7677041d9c3SFlorian Zumbiehl static bool pl2303_termios_change(const struct ktermios *a, const struct ktermios *b)
7687041d9c3SFlorian Zumbiehl {
7697041d9c3SFlorian Zumbiehl 	bool ixon_change;
7707041d9c3SFlorian Zumbiehl 
7717041d9c3SFlorian Zumbiehl 	ixon_change = ((a->c_iflag ^ b->c_iflag) & (IXON | IXANY)) ||
7727041d9c3SFlorian Zumbiehl 			a->c_cc[VSTART] != b->c_cc[VSTART] ||
7737041d9c3SFlorian Zumbiehl 			a->c_cc[VSTOP] != b->c_cc[VSTOP];
7747041d9c3SFlorian Zumbiehl 
7757041d9c3SFlorian Zumbiehl 	return tty_termios_hw_change(a, b) || ixon_change;
7767041d9c3SFlorian Zumbiehl }
7777041d9c3SFlorian Zumbiehl 
pl2303_enable_xonxoff(struct tty_struct * tty,const struct pl2303_type_data * type)77868270dabSJohan Hovold static bool pl2303_enable_xonxoff(struct tty_struct *tty, const struct pl2303_type_data *type)
77968270dabSJohan Hovold {
78068270dabSJohan Hovold 	if (!I_IXON(tty) || I_IXANY(tty))
78168270dabSJohan Hovold 		return false;
78268270dabSJohan Hovold 
78368270dabSJohan Hovold 	if (START_CHAR(tty) != 0x11 || STOP_CHAR(tty) != 0x13)
78468270dabSJohan Hovold 		return false;
78568270dabSJohan Hovold 
78668270dabSJohan Hovold 	if (type->no_autoxonxoff)
78768270dabSJohan Hovold 		return false;
78868270dabSJohan Hovold 
78968270dabSJohan Hovold 	return true;
79068270dabSJohan Hovold }
79168270dabSJohan Hovold 
pl2303_set_termios(struct tty_struct * tty,struct usb_serial_port * port,const struct ktermios * old_termios)79215e7ceadSJohan Hovold static void pl2303_set_termios(struct tty_struct *tty,
793f6d47fe5SIlpo Järvinen 			       struct usb_serial_port *port,
794f6d47fe5SIlpo Järvinen 			       const struct ktermios *old_termios)
79515e7ceadSJohan Hovold {
79615e7ceadSJohan Hovold 	struct usb_serial *serial = port->serial;
79715e7ceadSJohan Hovold 	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
79815e7ceadSJohan Hovold 	struct pl2303_private *priv = usb_get_serial_port_data(port);
79915e7ceadSJohan Hovold 	unsigned long flags;
80015e7ceadSJohan Hovold 	unsigned char *buf;
801383d19c5SJohan Hovold 	int ret;
80215e7ceadSJohan Hovold 	u8 control;
80315e7ceadSJohan Hovold 
8047041d9c3SFlorian Zumbiehl 	if (old_termios && !pl2303_termios_change(&tty->termios, old_termios))
80515e7ceadSJohan Hovold 		return;
80615e7ceadSJohan Hovold 
80715e7ceadSJohan Hovold 	buf = kzalloc(7, GFP_KERNEL);
80815e7ceadSJohan Hovold 	if (!buf) {
80915e7ceadSJohan Hovold 		/* Report back no change occurred */
81015e7ceadSJohan Hovold 		if (old_termios)
81115e7ceadSJohan Hovold 			tty->termios = *old_termios;
81215e7ceadSJohan Hovold 		return;
81315e7ceadSJohan Hovold 	}
81415e7ceadSJohan Hovold 
815383d19c5SJohan Hovold 	pl2303_get_line_request(port, buf);
81615e7ceadSJohan Hovold 
8173ec2ff37SJiri Slaby 	buf[6] = tty_get_char_size(tty->termios.c_cflag);
81815e7ceadSJohan Hovold 	dev_dbg(&port->dev, "data bits = %d\n", buf[6]);
81915e7ceadSJohan Hovold 
820692ed4ddSGreg Kroah-Hartman 	/* For reference buf[0]:buf[3] baud rate value */
82179816824SJohan Hovold 	pl2303_encode_baud_rate(tty, port, &buf[0]);
82215e7ceadSJohan Hovold 
8231da177e4SLinus Torvalds 	/* For reference buf[4]=0 is 1 stop bits */
8241da177e4SLinus Torvalds 	/* For reference buf[4]=1 is 1.5 stop bits */
8251da177e4SLinus Torvalds 	/* For reference buf[4]=2 is 2 stop bits */
82687265b45SJohan Hovold 	if (C_CSTOPB(tty)) {
82787265b45SJohan Hovold 		/*
82887265b45SJohan Hovold 		 * NOTE: Comply with "real" UARTs / RS232:
82929cf1b72SFrank Schaefer 		 *       use 1.5 instead of 2 stop bits with 5 data bits
83029cf1b72SFrank Schaefer 		 */
83187265b45SJohan Hovold 		if (C_CSIZE(tty) == CS5) {
83229cf1b72SFrank Schaefer 			buf[4] = 1;
833d8789b2bSGreg Kroah-Hartman 			dev_dbg(&port->dev, "stop bits = 1.5\n");
83429cf1b72SFrank Schaefer 		} else {
8351da177e4SLinus Torvalds 			buf[4] = 2;
836d8789b2bSGreg Kroah-Hartman 			dev_dbg(&port->dev, "stop bits = 2\n");
83729cf1b72SFrank Schaefer 		}
8381da177e4SLinus Torvalds 	} else {
8391da177e4SLinus Torvalds 		buf[4] = 0;
840d8789b2bSGreg Kroah-Hartman 		dev_dbg(&port->dev, "stop bits = 1\n");
8411da177e4SLinus Torvalds 	}
8421da177e4SLinus Torvalds 
84387265b45SJohan Hovold 	if (C_PARENB(tty)) {
8441da177e4SLinus Torvalds 		/* For reference buf[5]=0 is none parity */
8451da177e4SLinus Torvalds 		/* For reference buf[5]=1 is odd parity */
8461da177e4SLinus Torvalds 		/* For reference buf[5]=2 is even parity */
8471da177e4SLinus Torvalds 		/* For reference buf[5]=3 is mark parity */
8481da177e4SLinus Torvalds 		/* For reference buf[5]=4 is space parity */
84987265b45SJohan Hovold 		if (C_PARODD(tty)) {
850619c4354SJohan Hovold 			if (C_CMSPAR(tty)) {
8516dd81b45SFrank Schaefer 				buf[5] = 3;
852d8789b2bSGreg Kroah-Hartman 				dev_dbg(&port->dev, "parity = mark\n");
8536dd81b45SFrank Schaefer 			} else {
8541da177e4SLinus Torvalds 				buf[5] = 1;
855d8789b2bSGreg Kroah-Hartman 				dev_dbg(&port->dev, "parity = odd\n");
8566dd81b45SFrank Schaefer 			}
8576dd81b45SFrank Schaefer 		} else {
858619c4354SJohan Hovold 			if (C_CMSPAR(tty)) {
8596dd81b45SFrank Schaefer 				buf[5] = 4;
860d8789b2bSGreg Kroah-Hartman 				dev_dbg(&port->dev, "parity = space\n");
8611da177e4SLinus Torvalds 			} else {
8621da177e4SLinus Torvalds 				buf[5] = 2;
863d8789b2bSGreg Kroah-Hartman 				dev_dbg(&port->dev, "parity = even\n");
8641da177e4SLinus Torvalds 			}
8656dd81b45SFrank Schaefer 		}
8661da177e4SLinus Torvalds 	} else {
8671da177e4SLinus Torvalds 		buf[5] = 0;
868d8789b2bSGreg Kroah-Hartman 		dev_dbg(&port->dev, "parity = none\n");
8691da177e4SLinus Torvalds 	}
8701da177e4SLinus Torvalds 
871623c8263SJohan Hovold 	/*
872623c8263SJohan Hovold 	 * Some PL2303 are known to lose bytes if you change serial settings
873623c8263SJohan Hovold 	 * even to the same values as before. Thus we actually need to filter
874623c8263SJohan Hovold 	 * in this specific case.
875623c8263SJohan Hovold 	 *
876623c8263SJohan Hovold 	 * Note that the tty_termios_hw_change check above is not sufficient
877623c8263SJohan Hovold 	 * as a previously requested baud rate may differ from the one
878623c8263SJohan Hovold 	 * actually used (and stored in old_termios).
879623c8263SJohan Hovold 	 *
880623c8263SJohan Hovold 	 * NOTE: No additional locking needed for line_settings as it is
881623c8263SJohan Hovold 	 *       only used in set_termios, which is serialised against itself.
882623c8263SJohan Hovold 	 */
883623c8263SJohan Hovold 	if (!old_termios || memcmp(buf, priv->line_settings, 7)) {
884383d19c5SJohan Hovold 		ret = pl2303_set_line_request(port, buf);
885383d19c5SJohan Hovold 		if (!ret)
886623c8263SJohan Hovold 			memcpy(priv->line_settings, buf, 7);
887623c8263SJohan Hovold 	}
888623c8263SJohan Hovold 
8891da177e4SLinus Torvalds 	/* change control lines if we are switching to or from B0 */
8901da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
8911da177e4SLinus Torvalds 	control = priv->line_control;
89287265b45SJohan Hovold 	if (C_BAUD(tty) == B0)
8931da177e4SLinus Torvalds 		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
8942d8f4447SJohan Hovold 	else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
8951da177e4SLinus Torvalds 		priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
8961da177e4SLinus Torvalds 	if (control != priv->line_control) {
8971da177e4SLinus Torvalds 		control = priv->line_control;
8981da177e4SLinus Torvalds 		spin_unlock_irqrestore(&priv->lock, flags);
899f45d0a5aSJohan Hovold 		pl2303_set_control_lines(port, control);
9001da177e4SLinus Torvalds 	} else {
9011da177e4SLinus Torvalds 		spin_unlock_irqrestore(&priv->lock, flags);
9021da177e4SLinus Torvalds 	}
9031da177e4SLinus Torvalds 
90487265b45SJohan Hovold 	if (C_CRTSCTS(tty)) {
905ebd09f1cSCharles Yeh 		if (spriv->quirks & PL2303_QUIRK_LEGACY) {
906f64c3ab2SJohan Hovold 			pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x40);
907ebd09f1cSCharles Yeh 		} else if (spriv->type == &pl2303_type_data[TYPE_HXN]) {
908ebd09f1cSCharles Yeh 			pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
909ebd09f1cSCharles Yeh 					PL2303_HXN_FLOWCTRL_MASK,
910ebd09f1cSCharles Yeh 					PL2303_HXN_FLOWCTRL_RTS_CTS);
911ebd09f1cSCharles Yeh 		} else {
912f64c3ab2SJohan Hovold 			pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x60);
913ebd09f1cSCharles Yeh 		}
91468270dabSJohan Hovold 	} else if (pl2303_enable_xonxoff(tty, spriv->type)) {
915ebd09f1cSCharles Yeh 		if (spriv->type == &pl2303_type_data[TYPE_HXN]) {
916ebd09f1cSCharles Yeh 			pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
917ebd09f1cSCharles Yeh 					PL2303_HXN_FLOWCTRL_MASK,
918ebd09f1cSCharles Yeh 					PL2303_HXN_FLOWCTRL_XON_XOFF);
919ebd09f1cSCharles Yeh 		} else {
920f64c3ab2SJohan Hovold 			pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0xc0);
921ebd09f1cSCharles Yeh 		}
922ebd09f1cSCharles Yeh 	} else {
923ebd09f1cSCharles Yeh 		if (spriv->type == &pl2303_type_data[TYPE_HXN]) {
924ebd09f1cSCharles Yeh 			pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
925ebd09f1cSCharles Yeh 					PL2303_HXN_FLOWCTRL_MASK,
926ebd09f1cSCharles Yeh 					PL2303_HXN_FLOWCTRL_NONE);
927715f9527St.sefzick 		} else {
928f64c3ab2SJohan Hovold 			pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0);
9291da177e4SLinus Torvalds 		}
930ebd09f1cSCharles Yeh 	}
9311da177e4SLinus Torvalds 
9321da177e4SLinus Torvalds 	kfree(buf);
9331da177e4SLinus Torvalds }
9341da177e4SLinus Torvalds 
pl2303_dtr_rts(struct usb_serial_port * port,int on)935335f8514SAlan Cox static void pl2303_dtr_rts(struct usb_serial_port *port, int on)
936572d3138SThiago Galesi {
937572d3138SThiago Galesi 	struct pl2303_private *priv = usb_get_serial_port_data(port);
938572d3138SThiago Galesi 	unsigned long flags;
939335f8514SAlan Cox 	u8 control;
940335f8514SAlan Cox 
941335f8514SAlan Cox 	spin_lock_irqsave(&priv->lock, flags);
942335f8514SAlan Cox 	if (on)
943335f8514SAlan Cox 		priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
944335f8514SAlan Cox 	else
945335f8514SAlan Cox 		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
946335f8514SAlan Cox 	control = priv->line_control;
947335f8514SAlan Cox 	spin_unlock_irqrestore(&priv->lock, flags);
948ccfe8188SJohan Hovold 
949f45d0a5aSJohan Hovold 	pl2303_set_control_lines(port, control);
950335f8514SAlan Cox }
951335f8514SAlan Cox 
pl2303_close(struct usb_serial_port * port)952335f8514SAlan Cox static void pl2303_close(struct usb_serial_port *port)
953335f8514SAlan Cox {
9548b0127b2SJohan Hovold 	usb_serial_generic_close(port);
955572d3138SThiago Galesi 	usb_kill_urb(port->interrupt_in_urb);
95652772a7fSJohan Hovold 	pl2303_set_break(port, false);
957572d3138SThiago Galesi }
958572d3138SThiago Galesi 
pl2303_open(struct tty_struct * tty,struct usb_serial_port * port)959a509a7e4SAlan Cox static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
9601da177e4SLinus Torvalds {
9611da177e4SLinus Torvalds 	struct usb_serial *serial = port->serial;
9628bf769ebSJohan Hovold 	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
9631da177e4SLinus Torvalds 	int result;
9641da177e4SLinus Torvalds 
96523c6acb9SJohan Hovold 	if (spriv->quirks & PL2303_QUIRK_LEGACY) {
9661da177e4SLinus Torvalds 		usb_clear_halt(serial->dev, port->write_urb->pipe);
9671da177e4SLinus Torvalds 		usb_clear_halt(serial->dev, port->read_urb->pipe);
9683e152505SSarah Sharp 	} else {
9691da177e4SLinus Torvalds 		/* reset upstream data pipes */
970ebd09f1cSCharles Yeh 		if (spriv->type == &pl2303_type_data[TYPE_HXN]) {
971ebd09f1cSCharles Yeh 			pl2303_vendor_write(serial, PL2303_HXN_RESET_REG,
972ebd09f1cSCharles Yeh 					PL2303_HXN_RESET_UPSTREAM_PIPE |
973ebd09f1cSCharles Yeh 					PL2303_HXN_RESET_DOWNSTREAM_PIPE);
974ebd09f1cSCharles Yeh 		} else {
975362eb026SJohan Hovold 			pl2303_vendor_write(serial, 8, 0);
976362eb026SJohan Hovold 			pl2303_vendor_write(serial, 9, 0);
9771da177e4SLinus Torvalds 		}
978ebd09f1cSCharles Yeh 	}
9791da177e4SLinus Torvalds 
9801da177e4SLinus Torvalds 	/* Setup termios */
98195da310eSAlan Cox 	if (tty)
9822d8f4447SJohan Hovold 		pl2303_set_termios(tty, port, NULL);
9831da177e4SLinus Torvalds 
9841da177e4SLinus Torvalds 	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
9851da177e4SLinus Torvalds 	if (result) {
986ccfe8188SJohan Hovold 		dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
987ccfe8188SJohan Hovold 			result);
988db6e9186SJohan Hovold 		return result;
9891da177e4SLinus Torvalds 	}
990d4691c3fSJohan Hovold 
991f5230a53SJohan Hovold 	result = usb_serial_generic_open(tty, port);
992d4691c3fSJohan Hovold 	if (result) {
993d4691c3fSJohan Hovold 		usb_kill_urb(port->interrupt_in_urb);
994d4691c3fSJohan Hovold 		return result;
995d4691c3fSJohan Hovold 	}
996d4691c3fSJohan Hovold 
9971da177e4SLinus Torvalds 	return 0;
9981da177e4SLinus Torvalds }
9991da177e4SLinus Torvalds 
pl2303_tiocmset(struct tty_struct * tty,unsigned int set,unsigned int clear)100020b9d177SAlan Cox static int pl2303_tiocmset(struct tty_struct *tty,
10011da177e4SLinus Torvalds 			   unsigned int set, unsigned int clear)
10021da177e4SLinus Torvalds {
100395da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
10041da177e4SLinus Torvalds 	struct pl2303_private *priv = usb_get_serial_port_data(port);
10051da177e4SLinus Torvalds 	unsigned long flags;
10061da177e4SLinus Torvalds 	u8 control;
10076f1efd6cSJohan Hovold 	int ret;
10086fdd8e8eSFlavio Leitner 
10091da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
10101da177e4SLinus Torvalds 	if (set & TIOCM_RTS)
10111da177e4SLinus Torvalds 		priv->line_control |= CONTROL_RTS;
10121da177e4SLinus Torvalds 	if (set & TIOCM_DTR)
10131da177e4SLinus Torvalds 		priv->line_control |= CONTROL_DTR;
10141da177e4SLinus Torvalds 	if (clear & TIOCM_RTS)
10151da177e4SLinus Torvalds 		priv->line_control &= ~CONTROL_RTS;
10161da177e4SLinus Torvalds 	if (clear & TIOCM_DTR)
10171da177e4SLinus Torvalds 		priv->line_control &= ~CONTROL_DTR;
10181da177e4SLinus Torvalds 	control = priv->line_control;
10191da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
10201da177e4SLinus Torvalds 
1021f45d0a5aSJohan Hovold 	ret = pl2303_set_control_lines(port, control);
1022aff5b323SJohan Hovold 	if (ret)
10235ddbb26bSJohan Hovold 		return usb_translate_errors(ret);
10246f1efd6cSJohan Hovold 
10255ddbb26bSJohan Hovold 	return 0;
10261da177e4SLinus Torvalds }
10271da177e4SLinus Torvalds 
pl2303_tiocmget(struct tty_struct * tty)102860b33c13SAlan Cox static int pl2303_tiocmget(struct tty_struct *tty)
10291da177e4SLinus Torvalds {
103095da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
10311da177e4SLinus Torvalds 	struct pl2303_private *priv = usb_get_serial_port_data(port);
10321da177e4SLinus Torvalds 	unsigned long flags;
10331da177e4SLinus Torvalds 	unsigned int mcr;
10341da177e4SLinus Torvalds 	unsigned int status;
10351da177e4SLinus Torvalds 	unsigned int result;
10361da177e4SLinus Torvalds 
10371da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
10381da177e4SLinus Torvalds 	mcr = priv->line_control;
10391da177e4SLinus Torvalds 	status = priv->line_status;
10401da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
10411da177e4SLinus Torvalds 
10421da177e4SLinus Torvalds 	result = ((mcr & CONTROL_DTR)		? TIOCM_DTR : 0)
10431da177e4SLinus Torvalds 		  | ((mcr & CONTROL_RTS)	? TIOCM_RTS : 0)
10441da177e4SLinus Torvalds 		  | ((status & UART_CTS)	? TIOCM_CTS : 0)
10451da177e4SLinus Torvalds 		  | ((status & UART_DSR)	? TIOCM_DSR : 0)
10461da177e4SLinus Torvalds 		  | ((status & UART_RING)	? TIOCM_RI  : 0)
10471da177e4SLinus Torvalds 		  | ((status & UART_DCD)	? TIOCM_CD  : 0);
10481da177e4SLinus Torvalds 
1049d8789b2bSGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds 	return result;
10521da177e4SLinus Torvalds }
10531da177e4SLinus Torvalds 
pl2303_carrier_raised(struct usb_serial_port * port)1054335f8514SAlan Cox static int pl2303_carrier_raised(struct usb_serial_port *port)
1055335f8514SAlan Cox {
1056335f8514SAlan Cox 	struct pl2303_private *priv = usb_get_serial_port_data(port);
1057ccfe8188SJohan Hovold 
1058335f8514SAlan Cox 	if (priv->line_status & UART_DCD)
1059335f8514SAlan Cox 		return 1;
1060ccfe8188SJohan Hovold 
1061335f8514SAlan Cox 	return 0;
1062335f8514SAlan Cox }
1063335f8514SAlan Cox 
pl2303_set_break(struct usb_serial_port * port,bool enable)10646ff58ae1SJohan Hovold static int pl2303_set_break(struct usb_serial_port *port, bool enable)
10651da177e4SLinus Torvalds {
10661da177e4SLinus Torvalds 	struct usb_serial *serial = port->serial;
10671da177e4SLinus Torvalds 	u16 state;
10681da177e4SLinus Torvalds 	int result;
10691da177e4SLinus Torvalds 
107052772a7fSJohan Hovold 	if (enable)
10711da177e4SLinus Torvalds 		state = BREAK_ON;
107252772a7fSJohan Hovold 	else
107352772a7fSJohan Hovold 		state = BREAK_OFF;
1074ccfe8188SJohan Hovold 
1075d8789b2bSGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - turning break %s\n", __func__,
10763a0f43e9SAlan Cox 			state == BREAK_OFF ? "off" : "on");
10771da177e4SLinus Torvalds 
10781da177e4SLinus Torvalds 	result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
10791da177e4SLinus Torvalds 				 BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
10801da177e4SLinus Torvalds 				 0, NULL, 0, 100);
10816ff58ae1SJohan Hovold 	if (result) {
1082d8789b2bSGreg Kroah-Hartman 		dev_err(&port->dev, "error sending break = %d\n", result);
10836ff58ae1SJohan Hovold 		return result;
10841da177e4SLinus Torvalds 	}
10851da177e4SLinus Torvalds 
10866ff58ae1SJohan Hovold 	return 0;
10876ff58ae1SJohan Hovold }
10886ff58ae1SJohan Hovold 
pl2303_break_ctl(struct tty_struct * tty,int state)10896ff58ae1SJohan Hovold static int pl2303_break_ctl(struct tty_struct *tty, int state)
109052772a7fSJohan Hovold {
109152772a7fSJohan Hovold 	struct usb_serial_port *port = tty->driver_data;
109252772a7fSJohan Hovold 
10936ff58ae1SJohan Hovold 	return pl2303_set_break(port, state);
109452772a7fSJohan Hovold }
109552772a7fSJohan Hovold 
pl2303_update_line_status(struct usb_serial_port * port,unsigned char * data,unsigned int actual_length)109697bb13ecSFlavio Leitner static void pl2303_update_line_status(struct usb_serial_port *port,
109797bb13ecSFlavio Leitner 				      unsigned char *data,
109897bb13ecSFlavio Leitner 				      unsigned int actual_length)
109997bb13ecSFlavio Leitner {
1100228e4105SJohan Hovold 	struct usb_serial *serial = port->serial;
1101228e4105SJohan Hovold 	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
110297bb13ecSFlavio Leitner 	struct pl2303_private *priv = usb_get_serial_port_data(port);
1103d14fc1a7SLibor Pechacek 	struct tty_struct *tty;
110497bb13ecSFlavio Leitner 	unsigned long flags;
1105228e4105SJohan Hovold 	unsigned int status_idx = UART_STATE_INDEX;
11066020c3beSJohan Hovold 	u8 status;
11076020c3beSJohan Hovold 	u8 delta;
110897bb13ecSFlavio Leitner 
1109228e4105SJohan Hovold 	if (spriv->quirks & PL2303_QUIRK_UART_STATE_IDX0)
111097bb13ecSFlavio Leitner 		status_idx = 0;
111197bb13ecSFlavio Leitner 
1112228e4105SJohan Hovold 	if (actual_length < status_idx + 1)
1113a009b75aSLuiz Fernando N. Capitulino 		return;
111497bb13ecSFlavio Leitner 
11156020c3beSJohan Hovold 	status = data[status_idx];
11166020c3beSJohan Hovold 
111797bb13ecSFlavio Leitner 	/* Save off the uart status for others to look at */
111897bb13ecSFlavio Leitner 	spin_lock_irqsave(&priv->lock, flags);
11196020c3beSJohan Hovold 	delta = priv->line_status ^ status;
11206020c3beSJohan Hovold 	priv->line_status = status;
112197bb13ecSFlavio Leitner 	spin_unlock_irqrestore(&priv->lock, flags);
1122ccfe8188SJohan Hovold 
11236020c3beSJohan Hovold 	if (status & UART_BREAK_ERROR)
1124430eb0d2SJason Wessel 		usb_serial_handle_break(port);
1125ccfe8188SJohan Hovold 
1126dbfd2866SJohan Hovold 	if (delta & UART_STATE_MSR_MASK) {
1127a4bcb294SJohan Hovold 		if (delta & UART_CTS)
1128a4bcb294SJohan Hovold 			port->icount.cts++;
1129a4bcb294SJohan Hovold 		if (delta & UART_DSR)
1130a4bcb294SJohan Hovold 			port->icount.dsr++;
1131a4bcb294SJohan Hovold 		if (delta & UART_RING)
1132a4bcb294SJohan Hovold 			port->icount.rng++;
11336020c3beSJohan Hovold 		if (delta & UART_DCD) {
1134a4bcb294SJohan Hovold 			port->icount.dcd++;
1135d14fc1a7SLibor Pechacek 			tty = tty_port_tty_get(&port->port);
11366020c3beSJohan Hovold 			if (tty) {
1137d14fc1a7SLibor Pechacek 				usb_serial_handle_dcd_change(port, tty,
11386020c3beSJohan Hovold 							status & UART_DCD);
1139d14fc1a7SLibor Pechacek 				tty_kref_put(tty);
114097bb13ecSFlavio Leitner 			}
11416020c3beSJohan Hovold 		}
1142dbfd2866SJohan Hovold 
1143dbfd2866SJohan Hovold 		wake_up_interruptible(&port->port.delta_msr_wait);
1144dbfd2866SJohan Hovold 	}
11456020c3beSJohan Hovold }
11461da177e4SLinus Torvalds 
pl2303_read_int_callback(struct urb * urb)11477d12e780SDavid Howells static void pl2303_read_int_callback(struct urb *urb)
11481da177e4SLinus Torvalds {
1149cdc97792SMing Lei 	struct usb_serial_port *port =  urb->context;
11501da177e4SLinus Torvalds 	unsigned char *data = urb->transfer_buffer;
115197bb13ecSFlavio Leitner 	unsigned int actual_length = urb->actual_length;
1152461d696aSGreg Kroah-Hartman 	int status = urb->status;
1153461d696aSGreg Kroah-Hartman 	int retval;
11541da177e4SLinus Torvalds 
1155461d696aSGreg Kroah-Hartman 	switch (status) {
11561da177e4SLinus Torvalds 	case 0:
11571da177e4SLinus Torvalds 		/* success */
11581da177e4SLinus Torvalds 		break;
11591da177e4SLinus Torvalds 	case -ECONNRESET:
11601da177e4SLinus Torvalds 	case -ENOENT:
11611da177e4SLinus Torvalds 	case -ESHUTDOWN:
11621da177e4SLinus Torvalds 		/* this urb is terminated, clean up */
1163d8789b2bSGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
1164d8789b2bSGreg Kroah-Hartman 			__func__, status);
11651da177e4SLinus Torvalds 		return;
11661da177e4SLinus Torvalds 	default:
1167d8789b2bSGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
1168d8789b2bSGreg Kroah-Hartman 			__func__, status);
11691da177e4SLinus Torvalds 		goto exit;
11701da177e4SLinus Torvalds 	}
11711da177e4SLinus Torvalds 
117259d33f2fSGreg Kroah-Hartman 	usb_serial_debug_data(&port->dev, __func__,
1173372db8a7SThiago Galesi 			      urb->actual_length, urb->transfer_buffer);
1174372db8a7SThiago Galesi 
117597bb13ecSFlavio Leitner 	pl2303_update_line_status(port, data, actual_length);
11761da177e4SLinus Torvalds 
11771da177e4SLinus Torvalds exit:
1178461d696aSGreg Kroah-Hartman 	retval = usb_submit_urb(urb, GFP_ATOMIC);
1179ccfe8188SJohan Hovold 	if (retval) {
1180d8789b2bSGreg Kroah-Hartman 		dev_err(&port->dev,
1181372db8a7SThiago Galesi 			"%s - usb_submit_urb failed with result %d\n",
1182441b62c1SHarvey Harrison 			__func__, retval);
11831da177e4SLinus Torvalds 	}
1184ccfe8188SJohan Hovold }
11851da177e4SLinus Torvalds 
pl2303_process_read_urb(struct urb * urb)1186f08e07acSJohan Hovold static void pl2303_process_read_urb(struct urb *urb)
1187d4fc4a7bSAlan Cox {
1188f08e07acSJohan Hovold 	struct usb_serial_port *port = urb->context;
1189f08e07acSJohan Hovold 	struct pl2303_private *priv = usb_get_serial_port_data(port);
1190d4fc4a7bSAlan Cox 	unsigned char *data = urb->transfer_buffer;
1191d4fc4a7bSAlan Cox 	char tty_flag = TTY_NORMAL;
1192f08e07acSJohan Hovold 	unsigned long flags;
1193f08e07acSJohan Hovold 	u8 line_status;
1194f08e07acSJohan Hovold 	int i;
1195f08e07acSJohan Hovold 
1196f08e07acSJohan Hovold 	/* update line status */
1197f08e07acSJohan Hovold 	spin_lock_irqsave(&priv->lock, flags);
1198f08e07acSJohan Hovold 	line_status = priv->line_status;
1199f08e07acSJohan Hovold 	priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
1200f08e07acSJohan Hovold 	spin_unlock_irqrestore(&priv->lock, flags);
1201f08e07acSJohan Hovold 
1202f08e07acSJohan Hovold 	if (!urb->actual_length)
1203f08e07acSJohan Hovold 		return;
1204f08e07acSJohan Hovold 
1205ccfe8188SJohan Hovold 	/*
1206ccfe8188SJohan Hovold 	 * Break takes precedence over parity, which takes precedence over
1207ccfe8188SJohan Hovold 	 * framing errors.
1208ccfe8188SJohan Hovold 	 */
1209d4fc4a7bSAlan Cox 	if (line_status & UART_BREAK_ERROR)
1210d4fc4a7bSAlan Cox 		tty_flag = TTY_BREAK;
1211d4fc4a7bSAlan Cox 	else if (line_status & UART_PARITY_ERROR)
1212d4fc4a7bSAlan Cox 		tty_flag = TTY_PARITY;
1213d4fc4a7bSAlan Cox 	else if (line_status & UART_FRAME_ERROR)
1214d4fc4a7bSAlan Cox 		tty_flag = TTY_FRAME;
1215d4fc4a7bSAlan Cox 
12163ba19fe3SJohan Hovold 	if (tty_flag != TTY_NORMAL)
12173ba19fe3SJohan Hovold 		dev_dbg(&port->dev, "%s - tty_flag = %d\n", __func__,
12183ba19fe3SJohan Hovold 								tty_flag);
1219d4fc4a7bSAlan Cox 	/* overrun is special, not associated with a char */
1220d4fc4a7bSAlan Cox 	if (line_status & UART_OVERRUN_ERROR)
122192a19f9cSJiri Slaby 		tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
12229388e2e7SJohan Hovold 
122337ae2315SJohan Hovold 	if (port->sysrq) {
1224d4fc4a7bSAlan Cox 		for (i = 0; i < urb->actual_length; ++i)
12256ee9f4b4SDmitry Torokhov 			if (!usb_serial_handle_sysrq_char(port, data[i]))
122692a19f9cSJiri Slaby 				tty_insert_flip_char(&port->port, data[i],
122792a19f9cSJiri Slaby 						tty_flag);
1228d45cc8dfSJohan Hovold 	} else {
12292f693357SJiri Slaby 		tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag,
1230d45cc8dfSJohan Hovold 							urb->actual_length);
12319388e2e7SJohan Hovold 	}
1232f08e07acSJohan Hovold 
12332e124b4aSJiri Slaby 	tty_flip_buffer_push(&port->port);
12341da177e4SLinus Torvalds }
12351da177e4SLinus Torvalds 
1236572d3138SThiago Galesi static struct usb_serial_driver pl2303_device = {
1237572d3138SThiago Galesi 	.driver = {
1238572d3138SThiago Galesi 		.owner =	THIS_MODULE,
1239572d3138SThiago Galesi 		.name =		"pl2303",
1240572d3138SThiago Galesi 	},
1241572d3138SThiago Galesi 	.id_table =		id_table,
1242590298b2SJohan Hovold 	.num_bulk_in =		1,
1243590298b2SJohan Hovold 	.num_bulk_out =		1,
12449fda620aSJohan Hovold 	.num_interrupt_in =	0,	/* see pl2303_calc_num_ports */
12457919c2fdSJohan Hovold 	.bulk_in_size =		256,
12463efeaff6SJohan Hovold 	.bulk_out_size =	256,
1247572d3138SThiago Galesi 	.open =			pl2303_open,
1248572d3138SThiago Galesi 	.close =		pl2303_close,
1249335f8514SAlan Cox 	.dtr_rts =		pl2303_dtr_rts,
1250335f8514SAlan Cox 	.carrier_raised =	pl2303_carrier_raised,
1251572d3138SThiago Galesi 	.break_ctl =		pl2303_break_ctl,
1252572d3138SThiago Galesi 	.set_termios =		pl2303_set_termios,
1253572d3138SThiago Galesi 	.tiocmget =		pl2303_tiocmget,
1254572d3138SThiago Galesi 	.tiocmset =		pl2303_tiocmset,
1255a4bcb294SJohan Hovold 	.tiocmiwait =		usb_serial_generic_tiocmiwait,
1256f08e07acSJohan Hovold 	.process_read_urb =	pl2303_process_read_urb,
1257572d3138SThiago Galesi 	.read_int_callback =	pl2303_read_int_callback,
1258228e4105SJohan Hovold 	.probe =		pl2303_probe,
12599fda620aSJohan Hovold 	.calc_num_ports =	pl2303_calc_num_ports,
1260572d3138SThiago Galesi 	.attach =		pl2303_startup,
1261f9c99bb8SAlan Stern 	.release =		pl2303_release,
12628bf769ebSJohan Hovold 	.port_probe =		pl2303_port_probe,
12638bf769ebSJohan Hovold 	.port_remove =		pl2303_port_remove,
1264572d3138SThiago Galesi };
12651da177e4SLinus Torvalds 
1266f667ddadSAlan Stern static struct usb_serial_driver * const serial_drivers[] = {
1267f667ddadSAlan Stern 	&pl2303_device, NULL
1268f667ddadSAlan Stern };
1269f667ddadSAlan Stern 
127068e24113SGreg Kroah-Hartman module_usb_serial_driver(serial_drivers, id_table);
12711da177e4SLinus Torvalds 
1272ccfe8188SJohan Hovold MODULE_DESCRIPTION("Prolific PL2303 USB to serial adaptor driver");
1273627cfa89SJohan Hovold MODULE_LICENSE("GPL v2");
1274