15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 26ce76104SFrank A Kingswood /* 36ce76104SFrank A Kingswood * Copyright 2007, Frank A Kingswood <frank@kingswood-consulting.co.uk> 4664d5df9SWerner Cornelius * Copyright 2007, Werner Cornelius <werner@cornelius-consult.de> 5664d5df9SWerner Cornelius * Copyright 2009, Boris Hajduk <boris@hajduk.org> 66ce76104SFrank A Kingswood * 76ce76104SFrank A Kingswood * ch341.c implements a serial port driver for the Winchiphead CH341. 86ce76104SFrank A Kingswood * 96ce76104SFrank A Kingswood * The CH341 device can be used to implement an RS232 asynchronous 106ce76104SFrank A Kingswood * serial port, an IEEE-1284 parallel printer port or a memory-like 116ce76104SFrank A Kingswood * interface. In all cases the CH341 supports an I2C interface as well. 126ce76104SFrank A Kingswood * This driver only supports the asynchronous serial interface. 136ce76104SFrank A Kingswood */ 146ce76104SFrank A Kingswood 156ce76104SFrank A Kingswood #include <linux/kernel.h> 166ce76104SFrank A Kingswood #include <linux/tty.h> 176ce76104SFrank A Kingswood #include <linux/module.h> 185a0e3ad6STejun Heo #include <linux/slab.h> 196ce76104SFrank A Kingswood #include <linux/usb.h> 206ce76104SFrank A Kingswood #include <linux/usb/serial.h> 216ce76104SFrank A Kingswood #include <linux/serial.h> 225be796f0SJohan Hovold #include <asm/unaligned.h> 236ce76104SFrank A Kingswood 24664d5df9SWerner Cornelius #define DEFAULT_BAUD_RATE 9600 256ce76104SFrank A Kingswood #define DEFAULT_TIMEOUT 1000 266ce76104SFrank A Kingswood 27664d5df9SWerner Cornelius /* flags for IO-Bits */ 28664d5df9SWerner Cornelius #define CH341_BIT_RTS (1 << 6) 29664d5df9SWerner Cornelius #define CH341_BIT_DTR (1 << 5) 30664d5df9SWerner Cornelius 31664d5df9SWerner Cornelius /******************************/ 32664d5df9SWerner Cornelius /* interrupt pipe definitions */ 33664d5df9SWerner Cornelius /******************************/ 34664d5df9SWerner Cornelius /* always 4 interrupt bytes */ 35664d5df9SWerner Cornelius /* first irq byte normally 0x08 */ 36664d5df9SWerner Cornelius /* second irq byte base 0x7d + below */ 37664d5df9SWerner Cornelius /* third irq byte base 0x94 + below */ 38664d5df9SWerner Cornelius /* fourth irq byte normally 0xee */ 39664d5df9SWerner Cornelius 40664d5df9SWerner Cornelius /* second interrupt byte */ 41664d5df9SWerner Cornelius #define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */ 42664d5df9SWerner Cornelius 43664d5df9SWerner Cornelius /* status returned in third interrupt answer byte, inverted in data 44664d5df9SWerner Cornelius from irq */ 45664d5df9SWerner Cornelius #define CH341_BIT_CTS 0x01 46664d5df9SWerner Cornelius #define CH341_BIT_DSR 0x02 47664d5df9SWerner Cornelius #define CH341_BIT_RI 0x04 48664d5df9SWerner Cornelius #define CH341_BIT_DCD 0x08 49664d5df9SWerner Cornelius #define CH341_BITS_MODEM_STAT 0x0f /* all bits */ 50664d5df9SWerner Cornelius 51492896f0STim Small /* Break support - the information used to implement this was gleaned from 52492896f0STim Small * the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato. 53492896f0STim Small */ 54492896f0STim Small 556fde8d29SAidan Thornton #define CH341_REQ_READ_VERSION 0x5F 56492896f0STim Small #define CH341_REQ_WRITE_REG 0x9A 57492896f0STim Small #define CH341_REQ_READ_REG 0x95 586fde8d29SAidan Thornton #define CH341_REQ_SERIAL_INIT 0xA1 596fde8d29SAidan Thornton #define CH341_REQ_MODEM_CTRL 0xA4 60492896f0STim Small 616fde8d29SAidan Thornton #define CH341_REG_BREAK 0x05 626fde8d29SAidan Thornton #define CH341_REG_LCR 0x18 636fde8d29SAidan Thornton #define CH341_NBREAK_BITS 0x01 646fde8d29SAidan Thornton 656fde8d29SAidan Thornton #define CH341_LCR_ENABLE_RX 0x80 666fde8d29SAidan Thornton #define CH341_LCR_ENABLE_TX 0x40 676fde8d29SAidan Thornton #define CH341_LCR_MARK_SPACE 0x20 686fde8d29SAidan Thornton #define CH341_LCR_PAR_EVEN 0x10 696fde8d29SAidan Thornton #define CH341_LCR_ENABLE_PAR 0x08 706fde8d29SAidan Thornton #define CH341_LCR_STOP_BITS_2 0x04 716fde8d29SAidan Thornton #define CH341_LCR_CS8 0x03 726fde8d29SAidan Thornton #define CH341_LCR_CS7 0x02 736fde8d29SAidan Thornton #define CH341_LCR_CS6 0x01 746fde8d29SAidan Thornton #define CH341_LCR_CS5 0x00 75492896f0STim Small 767d40d7e8SNémeth Márton static const struct usb_device_id id_table[] = { 776ce76104SFrank A Kingswood { USB_DEVICE(0x4348, 0x5523) }, 7882078234SMichael F. Robbins { USB_DEVICE(0x1a86, 0x7523) }, 79d0781383Swangyanqing { USB_DEVICE(0x1a86, 0x5523) }, 806ce76104SFrank A Kingswood { }, 816ce76104SFrank A Kingswood }; 826ce76104SFrank A Kingswood MODULE_DEVICE_TABLE(usb, id_table); 836ce76104SFrank A Kingswood 846ce76104SFrank A Kingswood struct ch341_private { 85664d5df9SWerner Cornelius spinlock_t lock; /* access lock */ 86664d5df9SWerner Cornelius unsigned baud_rate; /* set baud rate */ 87beea33d4SJohan Hovold u8 mcr; 88e8024460SJohan Hovold u8 msr; 893cca8624SJohan Hovold u8 lcr; 906ce76104SFrank A Kingswood }; 916ce76104SFrank A Kingswood 92aa91def4SNicolas PLANEL static void ch341_set_termios(struct tty_struct *tty, 93aa91def4SNicolas PLANEL struct usb_serial_port *port, 94aa91def4SNicolas PLANEL struct ktermios *old_termios); 95aa91def4SNicolas PLANEL 966ce76104SFrank A Kingswood static int ch341_control_out(struct usb_device *dev, u8 request, 976ce76104SFrank A Kingswood u16 value, u16 index) 986ce76104SFrank A Kingswood { 996ce76104SFrank A Kingswood int r; 10079cbeeafSGreg Kroah-Hartman 10191e0efcdSJohan Hovold dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x)\n", __func__, 10291e0efcdSJohan Hovold request, value, index); 1036ce76104SFrank A Kingswood 1046ce76104SFrank A Kingswood r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request, 1056ce76104SFrank A Kingswood USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 1066ce76104SFrank A Kingswood value, index, NULL, 0, DEFAULT_TIMEOUT); 1072d5a9c72SJohan Hovold if (r < 0) 1082d5a9c72SJohan Hovold dev_err(&dev->dev, "failed to send control message: %d\n", r); 1096ce76104SFrank A Kingswood 1106ce76104SFrank A Kingswood return r; 1116ce76104SFrank A Kingswood } 1126ce76104SFrank A Kingswood 1136ce76104SFrank A Kingswood static int ch341_control_in(struct usb_device *dev, 1146ce76104SFrank A Kingswood u8 request, u16 value, u16 index, 1156ce76104SFrank A Kingswood char *buf, unsigned bufsize) 1166ce76104SFrank A Kingswood { 1176ce76104SFrank A Kingswood int r; 11879cbeeafSGreg Kroah-Hartman 11991e0efcdSJohan Hovold dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x,%u)\n", __func__, 12091e0efcdSJohan Hovold request, value, index, bufsize); 1216ce76104SFrank A Kingswood 1226ce76104SFrank A Kingswood r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request, 1236ce76104SFrank A Kingswood USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 1246ce76104SFrank A Kingswood value, index, buf, bufsize, DEFAULT_TIMEOUT); 125e33eab9dSDan Carpenter if (r < (int)bufsize) { 1262d5a9c72SJohan Hovold if (r >= 0) { 1272d5a9c72SJohan Hovold dev_err(&dev->dev, 1282d5a9c72SJohan Hovold "short control message received (%d < %u)\n", 1292d5a9c72SJohan Hovold r, bufsize); 1302d5a9c72SJohan Hovold r = -EIO; 1312d5a9c72SJohan Hovold } 1322d5a9c72SJohan Hovold 1332d5a9c72SJohan Hovold dev_err(&dev->dev, "failed to receive control message: %d\n", 1342d5a9c72SJohan Hovold r); 1356ce76104SFrank A Kingswood return r; 1366ce76104SFrank A Kingswood } 1376ce76104SFrank A Kingswood 1382d5a9c72SJohan Hovold return 0; 1392d5a9c72SJohan Hovold } 1402d5a9c72SJohan Hovold 14135714565SJohan Hovold #define CH341_CLKRATE 48000000 14235714565SJohan Hovold #define CH341_CLK_DIV(ps, fact) (1 << (12 - 3 * (ps) - (fact))) 14335714565SJohan Hovold #define CH341_MIN_RATE(ps) (CH341_CLKRATE / (CH341_CLK_DIV((ps), 1) * 512)) 14435714565SJohan Hovold 14535714565SJohan Hovold static const speed_t ch341_min_rates[] = { 14635714565SJohan Hovold CH341_MIN_RATE(0), 14735714565SJohan Hovold CH341_MIN_RATE(1), 14835714565SJohan Hovold CH341_MIN_RATE(2), 14935714565SJohan Hovold CH341_MIN_RATE(3), 15035714565SJohan Hovold }; 15135714565SJohan Hovold 15235714565SJohan Hovold /* 15335714565SJohan Hovold * The device line speed is given by the following equation: 15435714565SJohan Hovold * 15535714565SJohan Hovold * baudrate = 48000000 / (2^(12 - 3 * ps - fact) * div), where 15635714565SJohan Hovold * 15735714565SJohan Hovold * 0 <= ps <= 3, 15835714565SJohan Hovold * 0 <= fact <= 1, 15935714565SJohan Hovold * 2 <= div <= 256 if fact = 0, or 16035714565SJohan Hovold * 9 <= div <= 256 if fact = 1 16135714565SJohan Hovold */ 16235714565SJohan Hovold static int ch341_get_divisor(speed_t speed) 16335714565SJohan Hovold { 16435714565SJohan Hovold unsigned int fact, div, clk_div; 16535714565SJohan Hovold int ps; 16635714565SJohan Hovold 16735714565SJohan Hovold /* 16835714565SJohan Hovold * Clamp to supported range, this makes the (ps < 0) and (div < 2) 16935714565SJohan Hovold * sanity checks below redundant. 17035714565SJohan Hovold */ 17135714565SJohan Hovold speed = clamp(speed, 46U, 3000000U); 17235714565SJohan Hovold 17335714565SJohan Hovold /* 17435714565SJohan Hovold * Start with highest possible base clock (fact = 1) that will give a 17535714565SJohan Hovold * divisor strictly less than 512. 17635714565SJohan Hovold */ 17735714565SJohan Hovold fact = 1; 17835714565SJohan Hovold for (ps = 3; ps >= 0; ps--) { 17935714565SJohan Hovold if (speed > ch341_min_rates[ps]) 18035714565SJohan Hovold break; 18135714565SJohan Hovold } 18235714565SJohan Hovold 18335714565SJohan Hovold if (ps < 0) 18435714565SJohan Hovold return -EINVAL; 18535714565SJohan Hovold 18635714565SJohan Hovold /* Determine corresponding divisor, rounding down. */ 18735714565SJohan Hovold clk_div = CH341_CLK_DIV(ps, fact); 18835714565SJohan Hovold div = CH341_CLKRATE / (clk_div * speed); 18935714565SJohan Hovold 19035714565SJohan Hovold /* Halve base clock (fact = 0) if required. */ 19135714565SJohan Hovold if (div < 9 || div > 255) { 19235714565SJohan Hovold div /= 2; 19335714565SJohan Hovold clk_div *= 2; 19435714565SJohan Hovold fact = 0; 19535714565SJohan Hovold } 19635714565SJohan Hovold 19735714565SJohan Hovold if (div < 2) 19835714565SJohan Hovold return -EINVAL; 19935714565SJohan Hovold 20035714565SJohan Hovold /* 20135714565SJohan Hovold * Pick next divisor if resulting rate is closer to the requested one, 20235714565SJohan Hovold * scale up to avoid rounding errors on low rates. 20335714565SJohan Hovold */ 20435714565SJohan Hovold if (16 * CH341_CLKRATE / (clk_div * div) - 16 * speed >= 20535714565SJohan Hovold 16 * speed - 16 * CH341_CLKRATE / (clk_div * (div + 1))) 20635714565SJohan Hovold div++; 20735714565SJohan Hovold 208*7c3d0228SJohan Hovold /* 209*7c3d0228SJohan Hovold * Prefer lower base clock (fact = 0) if even divisor. 210*7c3d0228SJohan Hovold * 211*7c3d0228SJohan Hovold * Note that this makes the receiver more tolerant to errors. 212*7c3d0228SJohan Hovold */ 213*7c3d0228SJohan Hovold if (fact == 1 && div % 2 == 0) { 214*7c3d0228SJohan Hovold div /= 2; 215*7c3d0228SJohan Hovold fact = 0; 216*7c3d0228SJohan Hovold } 217*7c3d0228SJohan Hovold 21835714565SJohan Hovold return (0x100 - div) << 8 | fact << 2 | ps; 21935714565SJohan Hovold } 22035714565SJohan Hovold 22155fa15b5SJohan Hovold static int ch341_set_baudrate_lcr(struct usb_device *dev, 22255fa15b5SJohan Hovold struct ch341_private *priv, u8 lcr) 2236ce76104SFrank A Kingswood { 22435714565SJohan Hovold int val; 2256ce76104SFrank A Kingswood int r; 2266ce76104SFrank A Kingswood 227664d5df9SWerner Cornelius if (!priv->baud_rate) 2286ce76104SFrank A Kingswood return -EINVAL; 229664d5df9SWerner Cornelius 23035714565SJohan Hovold val = ch341_get_divisor(priv->baud_rate); 23135714565SJohan Hovold if (val < 0) 232664d5df9SWerner Cornelius return -EINVAL; 233664d5df9SWerner Cornelius 23455fa15b5SJohan Hovold /* 23555fa15b5SJohan Hovold * CH341A buffers data until a full endpoint-size packet (32 bytes) 23655fa15b5SJohan Hovold * has been received unless bit 7 is set. 23755fa15b5SJohan Hovold */ 23835714565SJohan Hovold val |= BIT(7); 23955fa15b5SJohan Hovold 24035714565SJohan Hovold r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, val); 24155fa15b5SJohan Hovold if (r) 24255fa15b5SJohan Hovold return r; 24355fa15b5SJohan Hovold 24455fa15b5SJohan Hovold r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, lcr); 24555fa15b5SJohan Hovold if (r) 24655fa15b5SJohan Hovold return r; 2476ce76104SFrank A Kingswood 2486ce76104SFrank A Kingswood return r; 2496ce76104SFrank A Kingswood } 2506ce76104SFrank A Kingswood 251664d5df9SWerner Cornelius static int ch341_set_handshake(struct usb_device *dev, u8 control) 2526ce76104SFrank A Kingswood { 2536fde8d29SAidan Thornton return ch341_control_out(dev, CH341_REQ_MODEM_CTRL, ~control, 0); 2546ce76104SFrank A Kingswood } 2556ce76104SFrank A Kingswood 256664d5df9SWerner Cornelius static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) 2576ce76104SFrank A Kingswood { 2582d5a9c72SJohan Hovold const unsigned int size = 2; 2596ce76104SFrank A Kingswood char *buffer; 2606ce76104SFrank A Kingswood int r; 261664d5df9SWerner Cornelius unsigned long flags; 2626ce76104SFrank A Kingswood 2636ce76104SFrank A Kingswood buffer = kmalloc(size, GFP_KERNEL); 2646ce76104SFrank A Kingswood if (!buffer) 2656ce76104SFrank A Kingswood return -ENOMEM; 2666ce76104SFrank A Kingswood 2676fde8d29SAidan Thornton r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size); 2686ce76104SFrank A Kingswood if (r < 0) 2696ce76104SFrank A Kingswood goto out; 2706ce76104SFrank A Kingswood 271664d5df9SWerner Cornelius spin_lock_irqsave(&priv->lock, flags); 272e8024460SJohan Hovold priv->msr = (~(*buffer)) & CH341_BITS_MODEM_STAT; 273664d5df9SWerner Cornelius spin_unlock_irqrestore(&priv->lock, flags); 2746ce76104SFrank A Kingswood 2756ce76104SFrank A Kingswood out: kfree(buffer); 2766ce76104SFrank A Kingswood return r; 2776ce76104SFrank A Kingswood } 2786ce76104SFrank A Kingswood 2796ce76104SFrank A Kingswood /* -------------------------------------------------------------------------- */ 2806ce76104SFrank A Kingswood 28193b6497dSAdrian Bunk static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) 2826ce76104SFrank A Kingswood { 2832d5a9c72SJohan Hovold const unsigned int size = 2; 2846ce76104SFrank A Kingswood char *buffer; 2856ce76104SFrank A Kingswood int r; 2866ce76104SFrank A Kingswood 2876ce76104SFrank A Kingswood buffer = kmalloc(size, GFP_KERNEL); 2886ce76104SFrank A Kingswood if (!buffer) 2896ce76104SFrank A Kingswood return -ENOMEM; 2906ce76104SFrank A Kingswood 2916ce76104SFrank A Kingswood /* expect two bytes 0x27 0x00 */ 2926fde8d29SAidan Thornton r = ch341_control_in(dev, CH341_REQ_READ_VERSION, 0, 0, buffer, size); 2936ce76104SFrank A Kingswood if (r < 0) 2946ce76104SFrank A Kingswood goto out; 295a98b6900SAidan Thornton dev_dbg(&dev->dev, "Chip version: 0x%02x\n", buffer[0]); 2966ce76104SFrank A Kingswood 2976fde8d29SAidan Thornton r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0, 0); 2986ce76104SFrank A Kingswood if (r < 0) 2996ce76104SFrank A Kingswood goto out; 3006ce76104SFrank A Kingswood 30155fa15b5SJohan Hovold r = ch341_set_baudrate_lcr(dev, priv, priv->lcr); 3026ce76104SFrank A Kingswood if (r < 0) 3036ce76104SFrank A Kingswood goto out; 3046ce76104SFrank A Kingswood 305beea33d4SJohan Hovold r = ch341_set_handshake(dev, priv->mcr); 3066ce76104SFrank A Kingswood 3076ce76104SFrank A Kingswood out: kfree(buffer); 3086ce76104SFrank A Kingswood return r; 3096ce76104SFrank A Kingswood } 3106ce76104SFrank A Kingswood 311456c5be5SJohan Hovold static int ch341_port_probe(struct usb_serial_port *port) 3126ce76104SFrank A Kingswood { 3136ce76104SFrank A Kingswood struct ch341_private *priv; 3146ce76104SFrank A Kingswood int r; 3156ce76104SFrank A Kingswood 3166ce76104SFrank A Kingswood priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL); 3176ce76104SFrank A Kingswood if (!priv) 3186ce76104SFrank A Kingswood return -ENOMEM; 3196ce76104SFrank A Kingswood 320664d5df9SWerner Cornelius spin_lock_init(&priv->lock); 3216ce76104SFrank A Kingswood priv->baud_rate = DEFAULT_BAUD_RATE; 3227c61b0d5SJohan Hovold /* 3237c61b0d5SJohan Hovold * Some CH340 devices appear unable to change the initial LCR 3247c61b0d5SJohan Hovold * settings, so set a sane 8N1 default. 3257c61b0d5SJohan Hovold */ 3267c61b0d5SJohan Hovold priv->lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX | CH341_LCR_CS8; 3276ce76104SFrank A Kingswood 328456c5be5SJohan Hovold r = ch341_configure(port->serial->dev, priv); 3296ce76104SFrank A Kingswood if (r < 0) 3306ce76104SFrank A Kingswood goto error; 3316ce76104SFrank A Kingswood 332456c5be5SJohan Hovold usb_set_serial_port_data(port, priv); 3336ce76104SFrank A Kingswood return 0; 3346ce76104SFrank A Kingswood 3356ce76104SFrank A Kingswood error: kfree(priv); 3366ce76104SFrank A Kingswood return r; 3376ce76104SFrank A Kingswood } 3386ce76104SFrank A Kingswood 339456c5be5SJohan Hovold static int ch341_port_remove(struct usb_serial_port *port) 340456c5be5SJohan Hovold { 341456c5be5SJohan Hovold struct ch341_private *priv; 342456c5be5SJohan Hovold 343456c5be5SJohan Hovold priv = usb_get_serial_port_data(port); 344456c5be5SJohan Hovold kfree(priv); 345456c5be5SJohan Hovold 346456c5be5SJohan Hovold return 0; 347456c5be5SJohan Hovold } 348456c5be5SJohan Hovold 349335f8514SAlan Cox static int ch341_carrier_raised(struct usb_serial_port *port) 350335f8514SAlan Cox { 351335f8514SAlan Cox struct ch341_private *priv = usb_get_serial_port_data(port); 352e8024460SJohan Hovold if (priv->msr & CH341_BIT_DCD) 353335f8514SAlan Cox return 1; 354335f8514SAlan Cox return 0; 355335f8514SAlan Cox } 356335f8514SAlan Cox 357335f8514SAlan Cox static void ch341_dtr_rts(struct usb_serial_port *port, int on) 358664d5df9SWerner Cornelius { 359664d5df9SWerner Cornelius struct ch341_private *priv = usb_get_serial_port_data(port); 360664d5df9SWerner Cornelius unsigned long flags; 361664d5df9SWerner Cornelius 362335f8514SAlan Cox /* drop DTR and RTS */ 363335f8514SAlan Cox spin_lock_irqsave(&priv->lock, flags); 364335f8514SAlan Cox if (on) 365beea33d4SJohan Hovold priv->mcr |= CH341_BIT_RTS | CH341_BIT_DTR; 366335f8514SAlan Cox else 367beea33d4SJohan Hovold priv->mcr &= ~(CH341_BIT_RTS | CH341_BIT_DTR); 368335f8514SAlan Cox spin_unlock_irqrestore(&priv->lock, flags); 369beea33d4SJohan Hovold ch341_set_handshake(port->serial->dev, priv->mcr); 370335f8514SAlan Cox } 371335f8514SAlan Cox 372335f8514SAlan Cox static void ch341_close(struct usb_serial_port *port) 373335f8514SAlan Cox { 374f26788daSJohan Hovold usb_serial_generic_close(port); 375664d5df9SWerner Cornelius usb_kill_urb(port->interrupt_in_urb); 376664d5df9SWerner Cornelius } 377664d5df9SWerner Cornelius 378664d5df9SWerner Cornelius 3796ce76104SFrank A Kingswood /* open this device, set default parameters */ 380a509a7e4SAlan Cox static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) 3816ce76104SFrank A Kingswood { 382456c5be5SJohan Hovold struct ch341_private *priv = usb_get_serial_port_data(port); 3836ce76104SFrank A Kingswood int r; 3846ce76104SFrank A Kingswood 385aa91def4SNicolas PLANEL if (tty) 386aa91def4SNicolas PLANEL ch341_set_termios(tty, port, NULL); 3876ce76104SFrank A Kingswood 388d9a38a87SJohan Hovold dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__); 389664d5df9SWerner Cornelius r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 390664d5df9SWerner Cornelius if (r) { 391d9a38a87SJohan Hovold dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n", 392d9a38a87SJohan Hovold __func__, r); 393f2950b78SJohan Hovold return r; 394664d5df9SWerner Cornelius } 395664d5df9SWerner Cornelius 396a0467a96SJohan Hovold r = ch341_get_status(port->serial->dev, priv); 397a0467a96SJohan Hovold if (r < 0) { 398a0467a96SJohan Hovold dev_err(&port->dev, "failed to read modem status: %d\n", r); 399a0467a96SJohan Hovold goto err_kill_interrupt_urb; 400a0467a96SJohan Hovold } 401a0467a96SJohan Hovold 402a509a7e4SAlan Cox r = usb_serial_generic_open(tty, port); 403f2950b78SJohan Hovold if (r) 404f2950b78SJohan Hovold goto err_kill_interrupt_urb; 4056ce76104SFrank A Kingswood 406f2950b78SJohan Hovold return 0; 407f2950b78SJohan Hovold 408f2950b78SJohan Hovold err_kill_interrupt_urb: 409f2950b78SJohan Hovold usb_kill_urb(port->interrupt_in_urb); 410f2950b78SJohan Hovold 411f2950b78SJohan Hovold return r; 4126ce76104SFrank A Kingswood } 4136ce76104SFrank A Kingswood 4146ce76104SFrank A Kingswood /* Old_termios contains the original termios settings and 4156ce76104SFrank A Kingswood * tty->termios contains the new setting to be used. 4166ce76104SFrank A Kingswood */ 41795da310eSAlan Cox static void ch341_set_termios(struct tty_struct *tty, 41895da310eSAlan Cox struct usb_serial_port *port, struct ktermios *old_termios) 4196ce76104SFrank A Kingswood { 4206ce76104SFrank A Kingswood struct ch341_private *priv = usb_get_serial_port_data(port); 4216ce76104SFrank A Kingswood unsigned baud_rate; 422664d5df9SWerner Cornelius unsigned long flags; 423448b6dc5SJohan Hovold u8 lcr; 4244e46c410SAidan Thornton int r; 4254e46c410SAidan Thornton 4264e46c410SAidan Thornton /* redundant changes may cause the chip to lose bytes */ 4274e46c410SAidan Thornton if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) 4284e46c410SAidan Thornton return; 4296ce76104SFrank A Kingswood 4306ce76104SFrank A Kingswood baud_rate = tty_get_baud_rate(tty); 4316ce76104SFrank A Kingswood 432448b6dc5SJohan Hovold lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX; 433664d5df9SWerner Cornelius 434ba781bdfSAidan Thornton switch (C_CSIZE(tty)) { 435ba781bdfSAidan Thornton case CS5: 436448b6dc5SJohan Hovold lcr |= CH341_LCR_CS5; 437ba781bdfSAidan Thornton break; 438ba781bdfSAidan Thornton case CS6: 439448b6dc5SJohan Hovold lcr |= CH341_LCR_CS6; 440ba781bdfSAidan Thornton break; 441ba781bdfSAidan Thornton case CS7: 442448b6dc5SJohan Hovold lcr |= CH341_LCR_CS7; 443ba781bdfSAidan Thornton break; 444ba781bdfSAidan Thornton case CS8: 445448b6dc5SJohan Hovold lcr |= CH341_LCR_CS8; 446ba781bdfSAidan Thornton break; 447ba781bdfSAidan Thornton } 448ba781bdfSAidan Thornton 449ba781bdfSAidan Thornton if (C_PARENB(tty)) { 450448b6dc5SJohan Hovold lcr |= CH341_LCR_ENABLE_PAR; 451ba781bdfSAidan Thornton if (C_PARODD(tty) == 0) 452448b6dc5SJohan Hovold lcr |= CH341_LCR_PAR_EVEN; 453ba781bdfSAidan Thornton if (C_CMSPAR(tty)) 454448b6dc5SJohan Hovold lcr |= CH341_LCR_MARK_SPACE; 455ba781bdfSAidan Thornton } 456ba781bdfSAidan Thornton 457ba781bdfSAidan Thornton if (C_CSTOPB(tty)) 458448b6dc5SJohan Hovold lcr |= CH341_LCR_STOP_BITS_2; 4594e46c410SAidan Thornton 460664d5df9SWerner Cornelius if (baud_rate) { 461a20047f3SJohan Hovold priv->baud_rate = baud_rate; 462a20047f3SJohan Hovold 463448b6dc5SJohan Hovold r = ch341_set_baudrate_lcr(port->serial->dev, priv, lcr); 4644e46c410SAidan Thornton if (r < 0 && old_termios) { 4654e46c410SAidan Thornton priv->baud_rate = tty_termios_baud_rate(old_termios); 4664e46c410SAidan Thornton tty_termios_copy_hw(&tty->termios, old_termios); 4673cca8624SJohan Hovold } else if (r == 0) { 468448b6dc5SJohan Hovold priv->lcr = lcr; 4694e46c410SAidan Thornton } 4706ce76104SFrank A Kingswood } 4716ce76104SFrank A Kingswood 472030ee7aeSJohan Hovold spin_lock_irqsave(&priv->lock, flags); 473030ee7aeSJohan Hovold if (C_BAUD(tty) == B0) 474beea33d4SJohan Hovold priv->mcr &= ~(CH341_BIT_DTR | CH341_BIT_RTS); 475030ee7aeSJohan Hovold else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 476beea33d4SJohan Hovold priv->mcr |= (CH341_BIT_DTR | CH341_BIT_RTS); 477030ee7aeSJohan Hovold spin_unlock_irqrestore(&priv->lock, flags); 4786ce76104SFrank A Kingswood 479beea33d4SJohan Hovold ch341_set_handshake(port->serial->dev, priv->mcr); 480664d5df9SWerner Cornelius } 48173f59308SAlan Cox 482492896f0STim Small static void ch341_break_ctl(struct tty_struct *tty, int break_state) 483492896f0STim Small { 484492896f0STim Small const uint16_t ch341_break_reg = 4856fde8d29SAidan Thornton ((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK; 486492896f0STim Small struct usb_serial_port *port = tty->driver_data; 487492896f0STim Small int r; 488492896f0STim Small uint16_t reg_contents; 489f2b5cc83SJohan Hovold uint8_t *break_reg; 490492896f0STim Small 491f2b5cc83SJohan Hovold break_reg = kmalloc(2, GFP_KERNEL); 49210c642d0SJohan Hovold if (!break_reg) 493f2b5cc83SJohan Hovold return; 494f2b5cc83SJohan Hovold 495492896f0STim Small r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG, 496f2b5cc83SJohan Hovold ch341_break_reg, 0, break_reg, 2); 497492896f0STim Small if (r < 0) { 4986a9b15feSJohan Hovold dev_err(&port->dev, "%s - USB control read error (%d)\n", 4996a9b15feSJohan Hovold __func__, r); 500f2b5cc83SJohan Hovold goto out; 501492896f0STim Small } 50279cbeeafSGreg Kroah-Hartman dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n", 503492896f0STim Small __func__, break_reg[0], break_reg[1]); 504492896f0STim Small if (break_state != 0) { 50579cbeeafSGreg Kroah-Hartman dev_dbg(&port->dev, "%s - Enter break state requested\n", __func__); 5066fde8d29SAidan Thornton break_reg[0] &= ~CH341_NBREAK_BITS; 5076fde8d29SAidan Thornton break_reg[1] &= ~CH341_LCR_ENABLE_TX; 508492896f0STim Small } else { 50979cbeeafSGreg Kroah-Hartman dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__); 5106fde8d29SAidan Thornton break_reg[0] |= CH341_NBREAK_BITS; 5116fde8d29SAidan Thornton break_reg[1] |= CH341_LCR_ENABLE_TX; 512492896f0STim Small } 51379cbeeafSGreg Kroah-Hartman dev_dbg(&port->dev, "%s - New ch341 break register contents - reg1: %x, reg2: %x\n", 514492896f0STim Small __func__, break_reg[0], break_reg[1]); 5155be796f0SJohan Hovold reg_contents = get_unaligned_le16(break_reg); 516492896f0STim Small r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG, 517492896f0STim Small ch341_break_reg, reg_contents); 518492896f0STim Small if (r < 0) 5196a9b15feSJohan Hovold dev_err(&port->dev, "%s - USB control write error (%d)\n", 5206a9b15feSJohan Hovold __func__, r); 521f2b5cc83SJohan Hovold out: 522f2b5cc83SJohan Hovold kfree(break_reg); 523492896f0STim Small } 524492896f0STim Small 52520b9d177SAlan Cox static int ch341_tiocmset(struct tty_struct *tty, 526664d5df9SWerner Cornelius unsigned int set, unsigned int clear) 527664d5df9SWerner Cornelius { 528664d5df9SWerner Cornelius struct usb_serial_port *port = tty->driver_data; 529664d5df9SWerner Cornelius struct ch341_private *priv = usb_get_serial_port_data(port); 530664d5df9SWerner Cornelius unsigned long flags; 531664d5df9SWerner Cornelius u8 control; 532664d5df9SWerner Cornelius 533664d5df9SWerner Cornelius spin_lock_irqsave(&priv->lock, flags); 534664d5df9SWerner Cornelius if (set & TIOCM_RTS) 535beea33d4SJohan Hovold priv->mcr |= CH341_BIT_RTS; 536664d5df9SWerner Cornelius if (set & TIOCM_DTR) 537beea33d4SJohan Hovold priv->mcr |= CH341_BIT_DTR; 538664d5df9SWerner Cornelius if (clear & TIOCM_RTS) 539beea33d4SJohan Hovold priv->mcr &= ~CH341_BIT_RTS; 540664d5df9SWerner Cornelius if (clear & TIOCM_DTR) 541beea33d4SJohan Hovold priv->mcr &= ~CH341_BIT_DTR; 542beea33d4SJohan Hovold control = priv->mcr; 543664d5df9SWerner Cornelius spin_unlock_irqrestore(&priv->lock, flags); 544664d5df9SWerner Cornelius 545664d5df9SWerner Cornelius return ch341_set_handshake(port->serial->dev, control); 546664d5df9SWerner Cornelius } 547664d5df9SWerner Cornelius 548e8024460SJohan Hovold static void ch341_update_status(struct usb_serial_port *port, 549ac035628SJohan Hovold unsigned char *data, size_t len) 550ac035628SJohan Hovold { 551ac035628SJohan Hovold struct ch341_private *priv = usb_get_serial_port_data(port); 552b770081fSJohan Hovold struct tty_struct *tty; 553ac035628SJohan Hovold unsigned long flags; 554b770081fSJohan Hovold u8 status; 555b770081fSJohan Hovold u8 delta; 556ac035628SJohan Hovold 557ac035628SJohan Hovold if (len < 4) 558ac035628SJohan Hovold return; 559ac035628SJohan Hovold 560b770081fSJohan Hovold status = ~data[2] & CH341_BITS_MODEM_STAT; 561b770081fSJohan Hovold 562ac035628SJohan Hovold spin_lock_irqsave(&priv->lock, flags); 563e8024460SJohan Hovold delta = status ^ priv->msr; 564e8024460SJohan Hovold priv->msr = status; 565ac035628SJohan Hovold spin_unlock_irqrestore(&priv->lock, flags); 566ac035628SJohan Hovold 567fd74b0b1SJohan Hovold if (data[1] & CH341_MULT_STAT) 568fd74b0b1SJohan Hovold dev_dbg(&port->dev, "%s - multiple status change\n", __func__); 569fd74b0b1SJohan Hovold 570d984fe91SJohan Hovold if (!delta) 571d984fe91SJohan Hovold return; 572d984fe91SJohan Hovold 5735e409a26SJohan Hovold if (delta & CH341_BIT_CTS) 5745e409a26SJohan Hovold port->icount.cts++; 5755e409a26SJohan Hovold if (delta & CH341_BIT_DSR) 5765e409a26SJohan Hovold port->icount.dsr++; 5775e409a26SJohan Hovold if (delta & CH341_BIT_RI) 5785e409a26SJohan Hovold port->icount.rng++; 579b770081fSJohan Hovold if (delta & CH341_BIT_DCD) { 5805e409a26SJohan Hovold port->icount.dcd++; 581b770081fSJohan Hovold tty = tty_port_tty_get(&port->port); 582b770081fSJohan Hovold if (tty) { 583ac035628SJohan Hovold usb_serial_handle_dcd_change(port, tty, 584b770081fSJohan Hovold status & CH341_BIT_DCD); 585ac035628SJohan Hovold tty_kref_put(tty); 586ac035628SJohan Hovold } 587b770081fSJohan Hovold } 588ac035628SJohan Hovold 589ac035628SJohan Hovold wake_up_interruptible(&port->port.delta_msr_wait); 590ac035628SJohan Hovold } 591ac035628SJohan Hovold 592664d5df9SWerner Cornelius static void ch341_read_int_callback(struct urb *urb) 593664d5df9SWerner Cornelius { 594271ec2d2SJohan Hovold struct usb_serial_port *port = urb->context; 595664d5df9SWerner Cornelius unsigned char *data = urb->transfer_buffer; 596271ec2d2SJohan Hovold unsigned int len = urb->actual_length; 597664d5df9SWerner Cornelius int status; 598664d5df9SWerner Cornelius 599664d5df9SWerner Cornelius switch (urb->status) { 600664d5df9SWerner Cornelius case 0: 601664d5df9SWerner Cornelius /* success */ 602664d5df9SWerner Cornelius break; 603664d5df9SWerner Cornelius case -ECONNRESET: 604664d5df9SWerner Cornelius case -ENOENT: 605664d5df9SWerner Cornelius case -ESHUTDOWN: 606664d5df9SWerner Cornelius /* this urb is terminated, clean up */ 607271ec2d2SJohan Hovold dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n", 60879cbeeafSGreg Kroah-Hartman __func__, urb->status); 609664d5df9SWerner Cornelius return; 610664d5df9SWerner Cornelius default: 611271ec2d2SJohan Hovold dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", 61279cbeeafSGreg Kroah-Hartman __func__, urb->status); 613664d5df9SWerner Cornelius goto exit; 614664d5df9SWerner Cornelius } 615664d5df9SWerner Cornelius 616271ec2d2SJohan Hovold usb_serial_debug_data(&port->dev, __func__, len, data); 617e8024460SJohan Hovold ch341_update_status(port, data, len); 618664d5df9SWerner Cornelius exit: 619664d5df9SWerner Cornelius status = usb_submit_urb(urb, GFP_ATOMIC); 620271ec2d2SJohan Hovold if (status) { 621271ec2d2SJohan Hovold dev_err(&urb->dev->dev, "%s - usb_submit_urb failed: %d\n", 622664d5df9SWerner Cornelius __func__, status); 623664d5df9SWerner Cornelius } 624271ec2d2SJohan Hovold } 625664d5df9SWerner Cornelius 62660b33c13SAlan Cox static int ch341_tiocmget(struct tty_struct *tty) 627664d5df9SWerner Cornelius { 628664d5df9SWerner Cornelius struct usb_serial_port *port = tty->driver_data; 629664d5df9SWerner Cornelius struct ch341_private *priv = usb_get_serial_port_data(port); 630664d5df9SWerner Cornelius unsigned long flags; 631664d5df9SWerner Cornelius u8 mcr; 632664d5df9SWerner Cornelius u8 status; 633664d5df9SWerner Cornelius unsigned int result; 634664d5df9SWerner Cornelius 635664d5df9SWerner Cornelius spin_lock_irqsave(&priv->lock, flags); 636beea33d4SJohan Hovold mcr = priv->mcr; 637e8024460SJohan Hovold status = priv->msr; 638664d5df9SWerner Cornelius spin_unlock_irqrestore(&priv->lock, flags); 639664d5df9SWerner Cornelius 640664d5df9SWerner Cornelius result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0) 641664d5df9SWerner Cornelius | ((mcr & CH341_BIT_RTS) ? TIOCM_RTS : 0) 642664d5df9SWerner Cornelius | ((status & CH341_BIT_CTS) ? TIOCM_CTS : 0) 643664d5df9SWerner Cornelius | ((status & CH341_BIT_DSR) ? TIOCM_DSR : 0) 644664d5df9SWerner Cornelius | ((status & CH341_BIT_RI) ? TIOCM_RI : 0) 645664d5df9SWerner Cornelius | ((status & CH341_BIT_DCD) ? TIOCM_CD : 0); 646664d5df9SWerner Cornelius 64779cbeeafSGreg Kroah-Hartman dev_dbg(&port->dev, "%s - result = %x\n", __func__, result); 648664d5df9SWerner Cornelius 649664d5df9SWerner Cornelius return result; 6506ce76104SFrank A Kingswood } 6516ce76104SFrank A Kingswood 652622b80cfSGreg Kroah-Hartman static int ch341_reset_resume(struct usb_serial *serial) 6531ded7ea4SMing Lei { 654ce5e2928SJohan Hovold struct usb_serial_port *port = serial->port[0]; 6554d5ef53fSJohan Hovold struct ch341_private *priv; 656ce5e2928SJohan Hovold int ret; 6571ded7ea4SMing Lei 6584d5ef53fSJohan Hovold priv = usb_get_serial_port_data(port); 6594d5ef53fSJohan Hovold if (!priv) 6604d5ef53fSJohan Hovold return 0; 6614d5ef53fSJohan Hovold 6621ded7ea4SMing Lei /* reconfigure ch341 serial port after bus-reset */ 6632bfd1c96SGreg Kroah-Hartman ch341_configure(serial->dev, priv); 6641ded7ea4SMing Lei 665ce5e2928SJohan Hovold if (tty_port_initialized(&port->port)) { 666ce5e2928SJohan Hovold ret = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); 667ce5e2928SJohan Hovold if (ret) { 668ce5e2928SJohan Hovold dev_err(&port->dev, "failed to submit interrupt urb: %d\n", 669ce5e2928SJohan Hovold ret); 670ce5e2928SJohan Hovold return ret; 671ce5e2928SJohan Hovold } 672a0467a96SJohan Hovold 673a0467a96SJohan Hovold ret = ch341_get_status(port->serial->dev, priv); 674a0467a96SJohan Hovold if (ret < 0) { 675a0467a96SJohan Hovold dev_err(&port->dev, "failed to read modem status: %d\n", 676a0467a96SJohan Hovold ret); 677a0467a96SJohan Hovold } 678ce5e2928SJohan Hovold } 679ce5e2928SJohan Hovold 680ce5e2928SJohan Hovold return usb_serial_generic_resume(serial); 6811ded7ea4SMing Lei } 6821ded7ea4SMing Lei 6836ce76104SFrank A Kingswood static struct usb_serial_driver ch341_device = { 6846ce76104SFrank A Kingswood .driver = { 6856ce76104SFrank A Kingswood .owner = THIS_MODULE, 6866ce76104SFrank A Kingswood .name = "ch341-uart", 6876ce76104SFrank A Kingswood }, 6886ce76104SFrank A Kingswood .id_table = id_table, 6896ce76104SFrank A Kingswood .num_ports = 1, 6906ce76104SFrank A Kingswood .open = ch341_open, 691335f8514SAlan Cox .dtr_rts = ch341_dtr_rts, 692335f8514SAlan Cox .carrier_raised = ch341_carrier_raised, 693664d5df9SWerner Cornelius .close = ch341_close, 6946ce76104SFrank A Kingswood .set_termios = ch341_set_termios, 695492896f0STim Small .break_ctl = ch341_break_ctl, 696664d5df9SWerner Cornelius .tiocmget = ch341_tiocmget, 697664d5df9SWerner Cornelius .tiocmset = ch341_tiocmset, 6985e409a26SJohan Hovold .tiocmiwait = usb_serial_generic_tiocmiwait, 699664d5df9SWerner Cornelius .read_int_callback = ch341_read_int_callback, 700456c5be5SJohan Hovold .port_probe = ch341_port_probe, 701456c5be5SJohan Hovold .port_remove = ch341_port_remove, 7021c1eaba8SGreg Kroah-Hartman .reset_resume = ch341_reset_resume, 7036ce76104SFrank A Kingswood }; 7046ce76104SFrank A Kingswood 70508a4f6bcSAlan Stern static struct usb_serial_driver * const serial_drivers[] = { 70608a4f6bcSAlan Stern &ch341_device, NULL 70708a4f6bcSAlan Stern }; 70808a4f6bcSAlan Stern 70968e24113SGreg Kroah-Hartman module_usb_serial_driver(serial_drivers, id_table); 7106ce76104SFrank A Kingswood 711627cfa89SJohan Hovold MODULE_LICENSE("GPL v2"); 712