18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * USB Keyspan PDA / Xircom / Entrega Converter driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1999 - 2001 Greg Kroah-Hartman	<greg@kroah.com>
68c2ecf20Sopenharmony_ci * Copyright (C) 1999, 2000 Brian Warner	<warner@lothar.com>
78c2ecf20Sopenharmony_ci * Copyright (C) 2000 Al Borchers		<borchers@steinerpoint.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * See Documentation/usb/usb-serial.rst for more information on using this
108c2ecf20Sopenharmony_ci * driver
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/errno.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/tty.h>
188c2ecf20Sopenharmony_ci#include <linux/tty_driver.h>
198c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
228c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
238c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
248c2ecf20Sopenharmony_ci#include <linux/usb.h>
258c2ecf20Sopenharmony_ci#include <linux/usb/serial.h>
268c2ecf20Sopenharmony_ci#include <linux/usb/ezusb.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* make a simple define to handle if we are compiling keyspan_pda or xircom support */
298c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_SERIAL_KEYSPAN_PDA)
308c2ecf20Sopenharmony_ci	#define KEYSPAN
318c2ecf20Sopenharmony_ci#else
328c2ecf20Sopenharmony_ci	#undef KEYSPAN
338c2ecf20Sopenharmony_ci#endif
348c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_SERIAL_XIRCOM)
358c2ecf20Sopenharmony_ci	#define XIRCOM
368c2ecf20Sopenharmony_ci#else
378c2ecf20Sopenharmony_ci	#undef XIRCOM
388c2ecf20Sopenharmony_ci#endif
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Brian Warner <warner@lothar.com>"
418c2ecf20Sopenharmony_ci#define DRIVER_DESC "USB Keyspan PDA Converter driver"
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define KEYSPAN_TX_THRESHOLD	16
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistruct keyspan_pda_private {
468c2ecf20Sopenharmony_ci	int			tx_room;
478c2ecf20Sopenharmony_ci	int			tx_throttled;
488c2ecf20Sopenharmony_ci	struct work_struct	unthrottle_work;
498c2ecf20Sopenharmony_ci	struct usb_serial	*serial;
508c2ecf20Sopenharmony_ci	struct usb_serial_port	*port;
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define KEYSPAN_VENDOR_ID		0x06cd
558c2ecf20Sopenharmony_ci#define KEYSPAN_PDA_FAKE_ID		0x0103
568c2ecf20Sopenharmony_ci#define KEYSPAN_PDA_ID			0x0104 /* no clue */
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* For Xircom PGSDB9 and older Entrega version of the same device */
598c2ecf20Sopenharmony_ci#define XIRCOM_VENDOR_ID		0x085a
608c2ecf20Sopenharmony_ci#define XIRCOM_FAKE_ID			0x8027
618c2ecf20Sopenharmony_ci#define XIRCOM_FAKE_ID_2		0x8025 /* "PGMFHUB" serial */
628c2ecf20Sopenharmony_ci#define ENTREGA_VENDOR_ID		0x1645
638c2ecf20Sopenharmony_ci#define ENTREGA_FAKE_ID			0x8093
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_combined[] = {
668c2ecf20Sopenharmony_ci#ifdef KEYSPAN
678c2ecf20Sopenharmony_ci	{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_FAKE_ID) },
688c2ecf20Sopenharmony_ci#endif
698c2ecf20Sopenharmony_ci#ifdef XIRCOM
708c2ecf20Sopenharmony_ci	{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) },
718c2ecf20Sopenharmony_ci	{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) },
728c2ecf20Sopenharmony_ci	{ USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) },
738c2ecf20Sopenharmony_ci#endif
748c2ecf20Sopenharmony_ci	{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) },
758c2ecf20Sopenharmony_ci	{ }						/* Terminating entry */
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table_combined);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_std[] = {
818c2ecf20Sopenharmony_ci	{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) },
828c2ecf20Sopenharmony_ci	{ }						/* Terminating entry */
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#ifdef KEYSPAN
868c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_fake[] = {
878c2ecf20Sopenharmony_ci	{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_FAKE_ID) },
888c2ecf20Sopenharmony_ci	{ }						/* Terminating entry */
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci#endif
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#ifdef XIRCOM
938c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_fake_xircom[] = {
948c2ecf20Sopenharmony_ci	{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) },
958c2ecf20Sopenharmony_ci	{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) },
968c2ecf20Sopenharmony_ci	{ USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) },
978c2ecf20Sopenharmony_ci	{ }
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci#endif
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void keyspan_pda_request_unthrottle(struct work_struct *work)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct keyspan_pda_private *priv =
1048c2ecf20Sopenharmony_ci		container_of(work, struct keyspan_pda_private, unthrottle_work);
1058c2ecf20Sopenharmony_ci	struct usb_serial *serial = priv->serial;
1068c2ecf20Sopenharmony_ci	int result;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* ask the device to tell us when the tx buffer becomes
1098c2ecf20Sopenharmony_ci	   sufficiently empty */
1108c2ecf20Sopenharmony_ci	result = usb_control_msg(serial->dev,
1118c2ecf20Sopenharmony_ci				 usb_sndctrlpipe(serial->dev, 0),
1128c2ecf20Sopenharmony_ci				 7, /* request_unthrottle */
1138c2ecf20Sopenharmony_ci				 USB_TYPE_VENDOR | USB_RECIP_INTERFACE
1148c2ecf20Sopenharmony_ci				 | USB_DIR_OUT,
1158c2ecf20Sopenharmony_ci				 KEYSPAN_TX_THRESHOLD,
1168c2ecf20Sopenharmony_ci				 0, /* index */
1178c2ecf20Sopenharmony_ci				 NULL,
1188c2ecf20Sopenharmony_ci				 0,
1198c2ecf20Sopenharmony_ci				 2000);
1208c2ecf20Sopenharmony_ci	if (result < 0)
1218c2ecf20Sopenharmony_ci		dev_dbg(&serial->dev->dev, "%s - error %d from usb_control_msg\n",
1228c2ecf20Sopenharmony_ci			__func__, result);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic void keyspan_pda_rx_interrupt(struct urb *urb)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
1298c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
1308c2ecf20Sopenharmony_ci	unsigned int len = urb->actual_length;
1318c2ecf20Sopenharmony_ci	int retval;
1328c2ecf20Sopenharmony_ci	int status = urb->status;
1338c2ecf20Sopenharmony_ci	struct keyspan_pda_private *priv;
1348c2ecf20Sopenharmony_ci	unsigned long flags;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	switch (status) {
1398c2ecf20Sopenharmony_ci	case 0:
1408c2ecf20Sopenharmony_ci		/* success */
1418c2ecf20Sopenharmony_ci		break;
1428c2ecf20Sopenharmony_ci	case -ECONNRESET:
1438c2ecf20Sopenharmony_ci	case -ENOENT:
1448c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
1458c2ecf20Sopenharmony_ci		/* this urb is terminated, clean up */
1468c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status);
1478c2ecf20Sopenharmony_ci		return;
1488c2ecf20Sopenharmony_ci	default:
1498c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
1508c2ecf20Sopenharmony_ci		goto exit;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (len < 1) {
1548c2ecf20Sopenharmony_ci		dev_warn(&port->dev, "short message received\n");
1558c2ecf20Sopenharmony_ci		goto exit;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* see if the message is data or a status interrupt */
1598c2ecf20Sopenharmony_ci	switch (data[0]) {
1608c2ecf20Sopenharmony_ci	case 0:
1618c2ecf20Sopenharmony_ci		 /* rest of message is rx data */
1628c2ecf20Sopenharmony_ci		if (len < 2)
1638c2ecf20Sopenharmony_ci			break;
1648c2ecf20Sopenharmony_ci		tty_insert_flip_string(&port->port, data + 1, len - 1);
1658c2ecf20Sopenharmony_ci		tty_flip_buffer_push(&port->port);
1668c2ecf20Sopenharmony_ci		break;
1678c2ecf20Sopenharmony_ci	case 1:
1688c2ecf20Sopenharmony_ci		/* status interrupt */
1698c2ecf20Sopenharmony_ci		if (len < 2) {
1708c2ecf20Sopenharmony_ci			dev_warn(&port->dev, "short interrupt message received\n");
1718c2ecf20Sopenharmony_ci			break;
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "rx int, d1=%d\n", data[1]);
1748c2ecf20Sopenharmony_ci		switch (data[1]) {
1758c2ecf20Sopenharmony_ci		case 1: /* modemline change */
1768c2ecf20Sopenharmony_ci			break;
1778c2ecf20Sopenharmony_ci		case 2: /* tx unthrottle interrupt */
1788c2ecf20Sopenharmony_ci			spin_lock_irqsave(&port->lock, flags);
1798c2ecf20Sopenharmony_ci			priv->tx_throttled = 0;
1808c2ecf20Sopenharmony_ci			priv->tx_room = max(priv->tx_room, KEYSPAN_TX_THRESHOLD);
1818c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&port->lock, flags);
1828c2ecf20Sopenharmony_ci			/* queue up a wakeup at scheduler time */
1838c2ecf20Sopenharmony_ci			usb_serial_port_softint(port);
1848c2ecf20Sopenharmony_ci			break;
1858c2ecf20Sopenharmony_ci		default:
1868c2ecf20Sopenharmony_ci			break;
1878c2ecf20Sopenharmony_ci		}
1888c2ecf20Sopenharmony_ci		break;
1898c2ecf20Sopenharmony_ci	default:
1908c2ecf20Sopenharmony_ci		break;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ciexit:
1948c2ecf20Sopenharmony_ci	retval = usb_submit_urb(urb, GFP_ATOMIC);
1958c2ecf20Sopenharmony_ci	if (retval)
1968c2ecf20Sopenharmony_ci		dev_err(&port->dev,
1978c2ecf20Sopenharmony_ci			"%s - usb_submit_urb failed with result %d\n",
1988c2ecf20Sopenharmony_ci			__func__, retval);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void keyspan_pda_rx_throttle(struct tty_struct *tty)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	/* stop receiving characters. We just turn off the URB request, and
2058c2ecf20Sopenharmony_ci	   let chars pile up in the device. If we're doing hardware
2068c2ecf20Sopenharmony_ci	   flowcontrol, the device will signal the other end when its buffer
2078c2ecf20Sopenharmony_ci	   fills up. If we're doing XON/XOFF, this would be a good time to
2088c2ecf20Sopenharmony_ci	   send an XOFF, although it might make sense to foist that off
2098c2ecf20Sopenharmony_ci	   upon the device too. */
2108c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic void keyspan_pda_rx_unthrottle(struct tty_struct *tty)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
2198c2ecf20Sopenharmony_ci	/* just restart the receive interrupt URB */
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL))
2228c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n");
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	int rc;
2298c2ecf20Sopenharmony_ci	int bindex;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	switch (baud) {
2328c2ecf20Sopenharmony_ci	case 110:
2338c2ecf20Sopenharmony_ci		bindex = 0;
2348c2ecf20Sopenharmony_ci		break;
2358c2ecf20Sopenharmony_ci	case 300:
2368c2ecf20Sopenharmony_ci		bindex = 1;
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci	case 1200:
2398c2ecf20Sopenharmony_ci		bindex = 2;
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci	case 2400:
2428c2ecf20Sopenharmony_ci		bindex = 3;
2438c2ecf20Sopenharmony_ci		break;
2448c2ecf20Sopenharmony_ci	case 4800:
2458c2ecf20Sopenharmony_ci		bindex = 4;
2468c2ecf20Sopenharmony_ci		break;
2478c2ecf20Sopenharmony_ci	case 9600:
2488c2ecf20Sopenharmony_ci		bindex = 5;
2498c2ecf20Sopenharmony_ci		break;
2508c2ecf20Sopenharmony_ci	case 19200:
2518c2ecf20Sopenharmony_ci		bindex = 6;
2528c2ecf20Sopenharmony_ci		break;
2538c2ecf20Sopenharmony_ci	case 38400:
2548c2ecf20Sopenharmony_ci		bindex = 7;
2558c2ecf20Sopenharmony_ci		break;
2568c2ecf20Sopenharmony_ci	case 57600:
2578c2ecf20Sopenharmony_ci		bindex = 8;
2588c2ecf20Sopenharmony_ci		break;
2598c2ecf20Sopenharmony_ci	case 115200:
2608c2ecf20Sopenharmony_ci		bindex = 9;
2618c2ecf20Sopenharmony_ci		break;
2628c2ecf20Sopenharmony_ci	default:
2638c2ecf20Sopenharmony_ci		bindex = 5;	/* Default to 9600 */
2648c2ecf20Sopenharmony_ci		baud = 9600;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/* rather than figure out how to sleep while waiting for this
2688c2ecf20Sopenharmony_ci	   to complete, I just use the "legacy" API. */
2698c2ecf20Sopenharmony_ci	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
2708c2ecf20Sopenharmony_ci			     0, /* set baud */
2718c2ecf20Sopenharmony_ci			     USB_TYPE_VENDOR
2728c2ecf20Sopenharmony_ci			     | USB_RECIP_INTERFACE
2738c2ecf20Sopenharmony_ci			     | USB_DIR_OUT, /* type */
2748c2ecf20Sopenharmony_ci			     bindex, /* value */
2758c2ecf20Sopenharmony_ci			     0, /* index */
2768c2ecf20Sopenharmony_ci			     NULL, /* &data */
2778c2ecf20Sopenharmony_ci			     0, /* size */
2788c2ecf20Sopenharmony_ci			     2000); /* timeout */
2798c2ecf20Sopenharmony_ci	if (rc < 0)
2808c2ecf20Sopenharmony_ci		return 0;
2818c2ecf20Sopenharmony_ci	return baud;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
2888c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
2898c2ecf20Sopenharmony_ci	int value;
2908c2ecf20Sopenharmony_ci	int result;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (break_state == -1)
2938c2ecf20Sopenharmony_ci		value = 1; /* start break */
2948c2ecf20Sopenharmony_ci	else
2958c2ecf20Sopenharmony_ci		value = 0; /* clear break */
2968c2ecf20Sopenharmony_ci	result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
2978c2ecf20Sopenharmony_ci			4, /* set break */
2988c2ecf20Sopenharmony_ci			USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
2998c2ecf20Sopenharmony_ci			value, 0, NULL, 0, 2000);
3008c2ecf20Sopenharmony_ci	if (result < 0)
3018c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - error %d from usb_control_msg\n",
3028c2ecf20Sopenharmony_ci			__func__, result);
3038c2ecf20Sopenharmony_ci	/* there is something funky about this.. the TCSBRK that 'cu' performs
3048c2ecf20Sopenharmony_ci	   ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4
3058c2ecf20Sopenharmony_ci	   seconds apart, but it feels like the break sent isn't as long as it
3068c2ecf20Sopenharmony_ci	   is on /dev/ttyS0 */
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic void keyspan_pda_set_termios(struct tty_struct *tty,
3118c2ecf20Sopenharmony_ci		struct usb_serial_port *port, struct ktermios *old_termios)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
3148c2ecf20Sopenharmony_ci	speed_t speed;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* cflag specifies lots of stuff: number of stop bits, parity, number
3178c2ecf20Sopenharmony_ci	   of data bits, baud. What can the device actually handle?:
3188c2ecf20Sopenharmony_ci	   CSTOPB (1 stop bit or 2)
3198c2ecf20Sopenharmony_ci	   PARENB (parity)
3208c2ecf20Sopenharmony_ci	   CSIZE (5bit .. 8bit)
3218c2ecf20Sopenharmony_ci	   There is minimal hw support for parity (a PSW bit seems to hold the
3228c2ecf20Sopenharmony_ci	   parity of whatever is in the accumulator). The UART either deals
3238c2ecf20Sopenharmony_ci	   with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data,
3248c2ecf20Sopenharmony_ci	   1 special, stop). So, with firmware changes, we could do:
3258c2ecf20Sopenharmony_ci	   8N1: 10 bit
3268c2ecf20Sopenharmony_ci	   8N2: 11 bit, extra bit always (mark?)
3278c2ecf20Sopenharmony_ci	   8[EOMS]1: 11 bit, extra bit is parity
3288c2ecf20Sopenharmony_ci	   7[EOMS]1: 10 bit, b0/b7 is parity
3298c2ecf20Sopenharmony_ci	   7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?)
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	   HW flow control is dictated by the tty->termios.c_cflags & CRTSCTS
3328c2ecf20Sopenharmony_ci	   bit.
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	   For now, just do baud. */
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	speed = tty_get_baud_rate(tty);
3378c2ecf20Sopenharmony_ci	speed = keyspan_pda_setbaud(serial, speed);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (speed == 0) {
3408c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "can't handle requested baud rate\n");
3418c2ecf20Sopenharmony_ci		/* It hasn't changed so.. */
3428c2ecf20Sopenharmony_ci		speed = tty_termios_baud_rate(old_termios);
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci	/* Only speed can change so copy the old h/w parameters
3458c2ecf20Sopenharmony_ci	   then encode the new speed */
3468c2ecf20Sopenharmony_ci	tty_termios_copy_hw(&tty->termios, old_termios);
3478c2ecf20Sopenharmony_ci	tty_encode_baud_rate(tty, speed, speed);
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci/* modem control pins: DTR and RTS are outputs and can be controlled.
3528c2ecf20Sopenharmony_ci   DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be
3538c2ecf20Sopenharmony_ci   read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused */
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int keyspan_pda_get_modem_info(struct usb_serial *serial,
3568c2ecf20Sopenharmony_ci				      unsigned char *value)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	int rc;
3598c2ecf20Sopenharmony_ci	u8 *data;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	data = kmalloc(1, GFP_KERNEL);
3628c2ecf20Sopenharmony_ci	if (!data)
3638c2ecf20Sopenharmony_ci		return -ENOMEM;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
3668c2ecf20Sopenharmony_ci			     3, /* get pins */
3678c2ecf20Sopenharmony_ci			     USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN,
3688c2ecf20Sopenharmony_ci			     0, 0, data, 1, 2000);
3698c2ecf20Sopenharmony_ci	if (rc == 1)
3708c2ecf20Sopenharmony_ci		*value = *data;
3718c2ecf20Sopenharmony_ci	else if (rc >= 0)
3728c2ecf20Sopenharmony_ci		rc = -EIO;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	kfree(data);
3758c2ecf20Sopenharmony_ci	return rc;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int keyspan_pda_set_modem_info(struct usb_serial *serial,
3808c2ecf20Sopenharmony_ci				      unsigned char value)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	int rc;
3838c2ecf20Sopenharmony_ci	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
3848c2ecf20Sopenharmony_ci			     3, /* set pins */
3858c2ecf20Sopenharmony_ci			     USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_OUT,
3868c2ecf20Sopenharmony_ci			     value, 0, NULL, 0, 2000);
3878c2ecf20Sopenharmony_ci	return rc;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic int keyspan_pda_tiocmget(struct tty_struct *tty)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
3938c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
3948c2ecf20Sopenharmony_ci	int rc;
3958c2ecf20Sopenharmony_ci	unsigned char status;
3968c2ecf20Sopenharmony_ci	int value;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	rc = keyspan_pda_get_modem_info(serial, &status);
3998c2ecf20Sopenharmony_ci	if (rc < 0)
4008c2ecf20Sopenharmony_ci		return rc;
4018c2ecf20Sopenharmony_ci	value =
4028c2ecf20Sopenharmony_ci		((status & (1<<7)) ? TIOCM_DTR : 0) |
4038c2ecf20Sopenharmony_ci		((status & (1<<6)) ? TIOCM_CAR : 0) |
4048c2ecf20Sopenharmony_ci		((status & (1<<5)) ? TIOCM_RNG : 0) |
4058c2ecf20Sopenharmony_ci		((status & (1<<4)) ? TIOCM_DSR : 0) |
4068c2ecf20Sopenharmony_ci		((status & (1<<3)) ? TIOCM_CTS : 0) |
4078c2ecf20Sopenharmony_ci		((status & (1<<2)) ? TIOCM_RTS : 0);
4088c2ecf20Sopenharmony_ci	return value;
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic int keyspan_pda_tiocmset(struct tty_struct *tty,
4128c2ecf20Sopenharmony_ci				unsigned int set, unsigned int clear)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
4158c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
4168c2ecf20Sopenharmony_ci	int rc;
4178c2ecf20Sopenharmony_ci	unsigned char status;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	rc = keyspan_pda_get_modem_info(serial, &status);
4208c2ecf20Sopenharmony_ci	if (rc < 0)
4218c2ecf20Sopenharmony_ci		return rc;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (set & TIOCM_RTS)
4248c2ecf20Sopenharmony_ci		status |= (1<<2);
4258c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR)
4268c2ecf20Sopenharmony_ci		status |= (1<<7);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (clear & TIOCM_RTS)
4298c2ecf20Sopenharmony_ci		status &= ~(1<<2);
4308c2ecf20Sopenharmony_ci	if (clear & TIOCM_DTR)
4318c2ecf20Sopenharmony_ci		status &= ~(1<<7);
4328c2ecf20Sopenharmony_ci	rc = keyspan_pda_set_modem_info(serial, status);
4338c2ecf20Sopenharmony_ci	return rc;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic int keyspan_pda_write(struct tty_struct *tty,
4378c2ecf20Sopenharmony_ci	struct usb_serial_port *port, const unsigned char *buf, int count)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
4408c2ecf20Sopenharmony_ci	int request_unthrottle = 0;
4418c2ecf20Sopenharmony_ci	int rc = 0;
4428c2ecf20Sopenharmony_ci	struct keyspan_pda_private *priv;
4438c2ecf20Sopenharmony_ci	unsigned long flags;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
4468c2ecf20Sopenharmony_ci	/* guess how much room is left in the device's ring buffer, and if we
4478c2ecf20Sopenharmony_ci	   want to send more than that, check first, updating our notion of
4488c2ecf20Sopenharmony_ci	   what is left. If our write will result in no room left, ask the
4498c2ecf20Sopenharmony_ci	   device to give us an interrupt when the room available rises above
4508c2ecf20Sopenharmony_ci	   a threshold, and hold off all writers (eventually, those using
4518c2ecf20Sopenharmony_ci	   select() or poll() too) until we receive that unthrottle interrupt.
4528c2ecf20Sopenharmony_ci	   Block if we can't write anything at all, otherwise write as much as
4538c2ecf20Sopenharmony_ci	   we can. */
4548c2ecf20Sopenharmony_ci	if (count == 0) {
4558c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "write request of 0 bytes\n");
4568c2ecf20Sopenharmony_ci		return 0;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* we might block because of:
4608c2ecf20Sopenharmony_ci	   the TX urb is in-flight (wait until it completes)
4618c2ecf20Sopenharmony_ci	   the device is full (wait until it says there is room)
4628c2ecf20Sopenharmony_ci	*/
4638c2ecf20Sopenharmony_ci	spin_lock_irqsave(&port->lock, flags);
4648c2ecf20Sopenharmony_ci	if (!test_bit(0, &port->write_urbs_free) || priv->tx_throttled) {
4658c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&port->lock, flags);
4668c2ecf20Sopenharmony_ci		return 0;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci	clear_bit(0, &port->write_urbs_free);
4698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&port->lock, flags);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/* At this point the URB is in our control, nobody else can submit it
4728c2ecf20Sopenharmony_ci	   again (the only sudden transition was the one from EINPROGRESS to
4738c2ecf20Sopenharmony_ci	   finished).  Also, the tx process is not throttled. So we are
4748c2ecf20Sopenharmony_ci	   ready to write. */
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	/* Check if we might overrun the Tx buffer.   If so, ask the
4798c2ecf20Sopenharmony_ci	   device how much room it really has.  This is done only on
4808c2ecf20Sopenharmony_ci	   scheduler time, since usb_control_msg() sleeps. */
4818c2ecf20Sopenharmony_ci	if (count > priv->tx_room && !in_interrupt()) {
4828c2ecf20Sopenharmony_ci		u8 *room;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		room = kmalloc(1, GFP_KERNEL);
4858c2ecf20Sopenharmony_ci		if (!room) {
4868c2ecf20Sopenharmony_ci			rc = -ENOMEM;
4878c2ecf20Sopenharmony_ci			goto exit;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		rc = usb_control_msg(serial->dev,
4918c2ecf20Sopenharmony_ci				     usb_rcvctrlpipe(serial->dev, 0),
4928c2ecf20Sopenharmony_ci				     6, /* write_room */
4938c2ecf20Sopenharmony_ci				     USB_TYPE_VENDOR | USB_RECIP_INTERFACE
4948c2ecf20Sopenharmony_ci				     | USB_DIR_IN,
4958c2ecf20Sopenharmony_ci				     0, /* value: 0 means "remaining room" */
4968c2ecf20Sopenharmony_ci				     0, /* index */
4978c2ecf20Sopenharmony_ci				     room,
4988c2ecf20Sopenharmony_ci				     1,
4998c2ecf20Sopenharmony_ci				     2000);
5008c2ecf20Sopenharmony_ci		if (rc > 0) {
5018c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "roomquery says %d\n", *room);
5028c2ecf20Sopenharmony_ci			priv->tx_room = *room;
5038c2ecf20Sopenharmony_ci		}
5048c2ecf20Sopenharmony_ci		kfree(room);
5058c2ecf20Sopenharmony_ci		if (rc < 0) {
5068c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "roomquery failed\n");
5078c2ecf20Sopenharmony_ci			goto exit;
5088c2ecf20Sopenharmony_ci		}
5098c2ecf20Sopenharmony_ci		if (rc == 0) {
5108c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "roomquery returned 0 bytes\n");
5118c2ecf20Sopenharmony_ci			rc = -EIO; /* device didn't return any data */
5128c2ecf20Sopenharmony_ci			goto exit;
5138c2ecf20Sopenharmony_ci		}
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	if (count >= priv->tx_room) {
5178c2ecf20Sopenharmony_ci		/* we're about to completely fill the Tx buffer, so
5188c2ecf20Sopenharmony_ci		   we'll be throttled afterwards. */
5198c2ecf20Sopenharmony_ci		count = priv->tx_room;
5208c2ecf20Sopenharmony_ci		request_unthrottle = 1;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (count) {
5248c2ecf20Sopenharmony_ci		/* now transfer data */
5258c2ecf20Sopenharmony_ci		memcpy(port->write_urb->transfer_buffer, buf, count);
5268c2ecf20Sopenharmony_ci		/* send the data out the bulk port */
5278c2ecf20Sopenharmony_ci		port->write_urb->transfer_buffer_length = count;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		priv->tx_room -= count;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci		rc = usb_submit_urb(port->write_urb, GFP_ATOMIC);
5328c2ecf20Sopenharmony_ci		if (rc) {
5338c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "usb_submit_urb(write bulk) failed\n");
5348c2ecf20Sopenharmony_ci			goto exit;
5358c2ecf20Sopenharmony_ci		}
5368c2ecf20Sopenharmony_ci	} else {
5378c2ecf20Sopenharmony_ci		/* There wasn't any room left, so we are throttled until
5388c2ecf20Sopenharmony_ci		   the buffer empties a bit */
5398c2ecf20Sopenharmony_ci		request_unthrottle = 1;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (request_unthrottle) {
5438c2ecf20Sopenharmony_ci		priv->tx_throttled = 1; /* block writers */
5448c2ecf20Sopenharmony_ci		schedule_work(&priv->unthrottle_work);
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	rc = count;
5488c2ecf20Sopenharmony_ciexit:
5498c2ecf20Sopenharmony_ci	if (rc <= 0)
5508c2ecf20Sopenharmony_ci		set_bit(0, &port->write_urbs_free);
5518c2ecf20Sopenharmony_ci	return rc;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic void keyspan_pda_write_bulk_callback(struct urb *urb)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	set_bit(0, &port->write_urbs_free);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* queue up a wakeup at scheduler time */
5628c2ecf20Sopenharmony_ci	usb_serial_port_softint(port);
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic int keyspan_pda_write_room(struct tty_struct *tty)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
5698c2ecf20Sopenharmony_ci	struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
5708c2ecf20Sopenharmony_ci	unsigned long flags;
5718c2ecf20Sopenharmony_ci	int room = 0;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	spin_lock_irqsave(&port->lock, flags);
5748c2ecf20Sopenharmony_ci	if (test_bit(0, &port->write_urbs_free) && !priv->tx_throttled)
5758c2ecf20Sopenharmony_ci		room = priv->tx_room;
5768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&port->lock, flags);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	return room;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic int keyspan_pda_chars_in_buffer(struct tty_struct *tty)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
5848c2ecf20Sopenharmony_ci	struct keyspan_pda_private *priv;
5858c2ecf20Sopenharmony_ci	unsigned long flags;
5868c2ecf20Sopenharmony_ci	int ret = 0;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	/* when throttled, return at least WAKEUP_CHARS to tell select() (via
5918c2ecf20Sopenharmony_ci	   n_tty.c:normal_poll() ) that we're not writeable. */
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&port->lock, flags);
5948c2ecf20Sopenharmony_ci	if (!test_bit(0, &port->write_urbs_free) || priv->tx_throttled)
5958c2ecf20Sopenharmony_ci		ret = 256;
5968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&port->lock, flags);
5978c2ecf20Sopenharmony_ci	return ret;
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cistatic void keyspan_pda_dtr_rts(struct usb_serial_port *port, int on)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (on)
6068c2ecf20Sopenharmony_ci		keyspan_pda_set_modem_info(serial, (1 << 7) | (1 << 2));
6078c2ecf20Sopenharmony_ci	else
6088c2ecf20Sopenharmony_ci		keyspan_pda_set_modem_info(serial, 0);
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic int keyspan_pda_open(struct tty_struct *tty,
6138c2ecf20Sopenharmony_ci					struct usb_serial_port *port)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
6168c2ecf20Sopenharmony_ci	u8 *room;
6178c2ecf20Sopenharmony_ci	int rc = 0;
6188c2ecf20Sopenharmony_ci	struct keyspan_pda_private *priv;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/* find out how much room is in the Tx ring */
6218c2ecf20Sopenharmony_ci	room = kmalloc(1, GFP_KERNEL);
6228c2ecf20Sopenharmony_ci	if (!room)
6238c2ecf20Sopenharmony_ci		return -ENOMEM;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
6268c2ecf20Sopenharmony_ci			     6, /* write_room */
6278c2ecf20Sopenharmony_ci			     USB_TYPE_VENDOR | USB_RECIP_INTERFACE
6288c2ecf20Sopenharmony_ci			     | USB_DIR_IN,
6298c2ecf20Sopenharmony_ci			     0, /* value */
6308c2ecf20Sopenharmony_ci			     0, /* index */
6318c2ecf20Sopenharmony_ci			     room,
6328c2ecf20Sopenharmony_ci			     1,
6338c2ecf20Sopenharmony_ci			     2000);
6348c2ecf20Sopenharmony_ci	if (rc < 0) {
6358c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - roomquery failed\n", __func__);
6368c2ecf20Sopenharmony_ci		goto error;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci	if (rc == 0) {
6398c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - roomquery returned 0 bytes\n", __func__);
6408c2ecf20Sopenharmony_ci		rc = -EIO;
6418c2ecf20Sopenharmony_ci		goto error;
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
6448c2ecf20Sopenharmony_ci	priv->tx_room = *room;
6458c2ecf20Sopenharmony_ci	priv->tx_throttled = *room ? 0 : 1;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	/*Start reading from the device*/
6488c2ecf20Sopenharmony_ci	rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
6498c2ecf20Sopenharmony_ci	if (rc) {
6508c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - usb_submit_urb(read int) failed\n", __func__);
6518c2ecf20Sopenharmony_ci		goto error;
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_cierror:
6548c2ecf20Sopenharmony_ci	kfree(room);
6558c2ecf20Sopenharmony_ci	return rc;
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_cistatic void keyspan_pda_close(struct usb_serial_port *port)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	usb_kill_urb(port->write_urb);
6628c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	cancel_work_sync(&priv->unthrottle_work);
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci/* download the firmware to a "fake" device (pre-renumeration) */
6698c2ecf20Sopenharmony_cistatic int keyspan_pda_fake_startup(struct usb_serial *serial)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	const char *fw_name;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	/* download the firmware here ... */
6748c2ecf20Sopenharmony_ci	ezusb_fx1_set_reset(serial->dev, 1);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	if (0) { ; }
6778c2ecf20Sopenharmony_ci#ifdef KEYSPAN
6788c2ecf20Sopenharmony_ci	else if (le16_to_cpu(serial->dev->descriptor.idVendor) == KEYSPAN_VENDOR_ID)
6798c2ecf20Sopenharmony_ci		fw_name = "keyspan_pda/keyspan_pda.fw";
6808c2ecf20Sopenharmony_ci#endif
6818c2ecf20Sopenharmony_ci#ifdef XIRCOM
6828c2ecf20Sopenharmony_ci	else if ((le16_to_cpu(serial->dev->descriptor.idVendor) == XIRCOM_VENDOR_ID) ||
6838c2ecf20Sopenharmony_ci		 (le16_to_cpu(serial->dev->descriptor.idVendor) == ENTREGA_VENDOR_ID))
6848c2ecf20Sopenharmony_ci		fw_name = "keyspan_pda/xircom_pgs.fw";
6858c2ecf20Sopenharmony_ci#endif
6868c2ecf20Sopenharmony_ci	else {
6878c2ecf20Sopenharmony_ci		dev_err(&serial->dev->dev, "%s: unknown vendor, aborting.\n",
6888c2ecf20Sopenharmony_ci			__func__);
6898c2ecf20Sopenharmony_ci		return -ENODEV;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	if (ezusb_fx1_ihex_firmware_download(serial->dev, fw_name) < 0) {
6938c2ecf20Sopenharmony_ci		dev_err(&serial->dev->dev, "failed to load firmware \"%s\"\n",
6948c2ecf20Sopenharmony_ci			fw_name);
6958c2ecf20Sopenharmony_ci		return -ENOENT;
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	/* after downloading firmware Renumeration will occur in a
6998c2ecf20Sopenharmony_ci	  moment and the new device will bind to the real driver */
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	/* we want this device to fail to have a driver assigned to it. */
7028c2ecf20Sopenharmony_ci	return 1;
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci#ifdef KEYSPAN
7068c2ecf20Sopenharmony_ciMODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw");
7078c2ecf20Sopenharmony_ci#endif
7088c2ecf20Sopenharmony_ci#ifdef XIRCOM
7098c2ecf20Sopenharmony_ciMODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw");
7108c2ecf20Sopenharmony_ci#endif
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic int keyspan_pda_port_probe(struct usb_serial_port *port)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	struct keyspan_pda_private *priv;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	priv = kmalloc(sizeof(struct keyspan_pda_private), GFP_KERNEL);
7188c2ecf20Sopenharmony_ci	if (!priv)
7198c2ecf20Sopenharmony_ci		return -ENOMEM;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	INIT_WORK(&priv->unthrottle_work, keyspan_pda_request_unthrottle);
7228c2ecf20Sopenharmony_ci	priv->serial = port->serial;
7238c2ecf20Sopenharmony_ci	priv->port = port;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	usb_set_serial_port_data(port, priv);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	return 0;
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic int keyspan_pda_port_remove(struct usb_serial_port *port)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	struct keyspan_pda_private *priv;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
7358c2ecf20Sopenharmony_ci	kfree(priv);
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	return 0;
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci#ifdef KEYSPAN
7418c2ecf20Sopenharmony_cistatic struct usb_serial_driver keyspan_pda_fake_device = {
7428c2ecf20Sopenharmony_ci	.driver = {
7438c2ecf20Sopenharmony_ci		.owner =	THIS_MODULE,
7448c2ecf20Sopenharmony_ci		.name =		"keyspan_pda_pre",
7458c2ecf20Sopenharmony_ci	},
7468c2ecf20Sopenharmony_ci	.description =		"Keyspan PDA - (prerenumeration)",
7478c2ecf20Sopenharmony_ci	.id_table =		id_table_fake,
7488c2ecf20Sopenharmony_ci	.num_ports =		1,
7498c2ecf20Sopenharmony_ci	.attach =		keyspan_pda_fake_startup,
7508c2ecf20Sopenharmony_ci};
7518c2ecf20Sopenharmony_ci#endif
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci#ifdef XIRCOM
7548c2ecf20Sopenharmony_cistatic struct usb_serial_driver xircom_pgs_fake_device = {
7558c2ecf20Sopenharmony_ci	.driver = {
7568c2ecf20Sopenharmony_ci		.owner =	THIS_MODULE,
7578c2ecf20Sopenharmony_ci		.name =		"xircom_no_firm",
7588c2ecf20Sopenharmony_ci	},
7598c2ecf20Sopenharmony_ci	.description =		"Xircom / Entrega PGS - (prerenumeration)",
7608c2ecf20Sopenharmony_ci	.id_table =		id_table_fake_xircom,
7618c2ecf20Sopenharmony_ci	.num_ports =		1,
7628c2ecf20Sopenharmony_ci	.attach =		keyspan_pda_fake_startup,
7638c2ecf20Sopenharmony_ci};
7648c2ecf20Sopenharmony_ci#endif
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cistatic struct usb_serial_driver keyspan_pda_device = {
7678c2ecf20Sopenharmony_ci	.driver = {
7688c2ecf20Sopenharmony_ci		.owner =	THIS_MODULE,
7698c2ecf20Sopenharmony_ci		.name =		"keyspan_pda",
7708c2ecf20Sopenharmony_ci	},
7718c2ecf20Sopenharmony_ci	.description =		"Keyspan PDA",
7728c2ecf20Sopenharmony_ci	.id_table =		id_table_std,
7738c2ecf20Sopenharmony_ci	.num_ports =		1,
7748c2ecf20Sopenharmony_ci	.num_bulk_out =		1,
7758c2ecf20Sopenharmony_ci	.num_interrupt_in =	1,
7768c2ecf20Sopenharmony_ci	.dtr_rts =		keyspan_pda_dtr_rts,
7778c2ecf20Sopenharmony_ci	.open =			keyspan_pda_open,
7788c2ecf20Sopenharmony_ci	.close =		keyspan_pda_close,
7798c2ecf20Sopenharmony_ci	.write =		keyspan_pda_write,
7808c2ecf20Sopenharmony_ci	.write_room =		keyspan_pda_write_room,
7818c2ecf20Sopenharmony_ci	.write_bulk_callback = 	keyspan_pda_write_bulk_callback,
7828c2ecf20Sopenharmony_ci	.read_int_callback =	keyspan_pda_rx_interrupt,
7838c2ecf20Sopenharmony_ci	.chars_in_buffer =	keyspan_pda_chars_in_buffer,
7848c2ecf20Sopenharmony_ci	.throttle =		keyspan_pda_rx_throttle,
7858c2ecf20Sopenharmony_ci	.unthrottle =		keyspan_pda_rx_unthrottle,
7868c2ecf20Sopenharmony_ci	.set_termios =		keyspan_pda_set_termios,
7878c2ecf20Sopenharmony_ci	.break_ctl =		keyspan_pda_break_ctl,
7888c2ecf20Sopenharmony_ci	.tiocmget =		keyspan_pda_tiocmget,
7898c2ecf20Sopenharmony_ci	.tiocmset =		keyspan_pda_tiocmset,
7908c2ecf20Sopenharmony_ci	.port_probe =		keyspan_pda_port_probe,
7918c2ecf20Sopenharmony_ci	.port_remove =		keyspan_pda_port_remove,
7928c2ecf20Sopenharmony_ci};
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
7958c2ecf20Sopenharmony_ci	&keyspan_pda_device,
7968c2ecf20Sopenharmony_ci#ifdef KEYSPAN
7978c2ecf20Sopenharmony_ci	&keyspan_pda_fake_device,
7988c2ecf20Sopenharmony_ci#endif
7998c2ecf20Sopenharmony_ci#ifdef XIRCOM
8008c2ecf20Sopenharmony_ci	&xircom_pgs_fake_device,
8018c2ecf20Sopenharmony_ci#endif
8028c2ecf20Sopenharmony_ci	NULL
8038c2ecf20Sopenharmony_ci};
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table_combined);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
8088c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
8098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
810