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