18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  REINER SCT cyberJack pinpad/e-com USB Chipcard Reader Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2001  REINER SCT
68c2ecf20Sopenharmony_ci *  Author: Matthias Bruestle
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  Contact: support@reiner-sct.com (see MAINTAINERS)
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  This program is largely derived from work by the linux-usb group
118c2ecf20Sopenharmony_ci *  and associated source files.  Please see the usb/serial files for
128c2ecf20Sopenharmony_ci *  individual credits and copyrights.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *  Thanks to Greg Kroah-Hartman (greg@kroah.com) for his help and
158c2ecf20Sopenharmony_ci *  patience.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci *  In case of problems, please write to the contact e-mail address
188c2ecf20Sopenharmony_ci *  mentioned above.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci *  Please note that later models of the cyberjack reader family are
218c2ecf20Sopenharmony_ci *  supported by a libusb-based userspace device driver.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci *  Homepage: http://www.reiner-sct.de/support/treiber_cyberjack.php#linux
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/kernel.h>
288c2ecf20Sopenharmony_ci#include <linux/errno.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci#include <linux/tty.h>
318c2ecf20Sopenharmony_ci#include <linux/tty_driver.h>
328c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
338c2ecf20Sopenharmony_ci#include <linux/module.h>
348c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
358c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
368c2ecf20Sopenharmony_ci#include <linux/usb.h>
378c2ecf20Sopenharmony_ci#include <linux/usb/serial.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define CYBERJACK_LOCAL_BUF_SIZE 32
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Matthias Bruestle"
428c2ecf20Sopenharmony_ci#define DRIVER_DESC "REINER SCT cyberJack pinpad/e-com USB Chipcard Reader Driver"
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define CYBERJACK_VENDOR_ID	0x0C4B
468c2ecf20Sopenharmony_ci#define CYBERJACK_PRODUCT_ID	0x0100
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* Function prototypes */
498c2ecf20Sopenharmony_cistatic int cyberjack_port_probe(struct usb_serial_port *port);
508c2ecf20Sopenharmony_cistatic int cyberjack_port_remove(struct usb_serial_port *port);
518c2ecf20Sopenharmony_cistatic int  cyberjack_open(struct tty_struct *tty,
528c2ecf20Sopenharmony_ci	struct usb_serial_port *port);
538c2ecf20Sopenharmony_cistatic void cyberjack_close(struct usb_serial_port *port);
548c2ecf20Sopenharmony_cistatic int cyberjack_write(struct tty_struct *tty,
558c2ecf20Sopenharmony_ci	struct usb_serial_port *port, const unsigned char *buf, int count);
568c2ecf20Sopenharmony_cistatic int cyberjack_write_room(struct tty_struct *tty);
578c2ecf20Sopenharmony_cistatic void cyberjack_read_int_callback(struct urb *urb);
588c2ecf20Sopenharmony_cistatic void cyberjack_read_bulk_callback(struct urb *urb);
598c2ecf20Sopenharmony_cistatic void cyberjack_write_bulk_callback(struct urb *urb);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = {
628c2ecf20Sopenharmony_ci	{ USB_DEVICE(CYBERJACK_VENDOR_ID, CYBERJACK_PRODUCT_ID) },
638c2ecf20Sopenharmony_ci	{ }			/* Terminating entry */
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic struct usb_serial_driver cyberjack_device = {
698c2ecf20Sopenharmony_ci	.driver = {
708c2ecf20Sopenharmony_ci		.owner =	THIS_MODULE,
718c2ecf20Sopenharmony_ci		.name =		"cyberjack",
728c2ecf20Sopenharmony_ci	},
738c2ecf20Sopenharmony_ci	.description =		"Reiner SCT Cyberjack USB card reader",
748c2ecf20Sopenharmony_ci	.id_table =		id_table,
758c2ecf20Sopenharmony_ci	.num_ports =		1,
768c2ecf20Sopenharmony_ci	.num_bulk_out =		1,
778c2ecf20Sopenharmony_ci	.port_probe =		cyberjack_port_probe,
788c2ecf20Sopenharmony_ci	.port_remove =		cyberjack_port_remove,
798c2ecf20Sopenharmony_ci	.open =			cyberjack_open,
808c2ecf20Sopenharmony_ci	.close =		cyberjack_close,
818c2ecf20Sopenharmony_ci	.write =		cyberjack_write,
828c2ecf20Sopenharmony_ci	.write_room =		cyberjack_write_room,
838c2ecf20Sopenharmony_ci	.read_int_callback =	cyberjack_read_int_callback,
848c2ecf20Sopenharmony_ci	.read_bulk_callback =	cyberjack_read_bulk_callback,
858c2ecf20Sopenharmony_ci	.write_bulk_callback =	cyberjack_write_bulk_callback,
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
898c2ecf20Sopenharmony_ci	&cyberjack_device, NULL
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistruct cyberjack_private {
938c2ecf20Sopenharmony_ci	spinlock_t	lock;		/* Lock for SMP */
948c2ecf20Sopenharmony_ci	short		rdtodo;		/* Bytes still to read */
958c2ecf20Sopenharmony_ci	unsigned char	wrbuf[5*64];	/* Buffer for collecting data to write */
968c2ecf20Sopenharmony_ci	short		wrfilled;	/* Overall data size we already got */
978c2ecf20Sopenharmony_ci	short		wrsent;		/* Data already sent */
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int cyberjack_port_probe(struct usb_serial_port *port)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct cyberjack_private *priv;
1038c2ecf20Sopenharmony_ci	int result;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	priv = kmalloc(sizeof(struct cyberjack_private), GFP_KERNEL);
1068c2ecf20Sopenharmony_ci	if (!priv)
1078c2ecf20Sopenharmony_ci		return -ENOMEM;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	spin_lock_init(&priv->lock);
1108c2ecf20Sopenharmony_ci	priv->rdtodo = 0;
1118c2ecf20Sopenharmony_ci	priv->wrfilled = 0;
1128c2ecf20Sopenharmony_ci	priv->wrsent = 0;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	usb_set_serial_port_data(port, priv);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
1178c2ecf20Sopenharmony_ci	if (result)
1188c2ecf20Sopenharmony_ci		dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int cyberjack_port_remove(struct usb_serial_port *port)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct cyberjack_private *priv;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
1308c2ecf20Sopenharmony_ci	kfree(priv);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int  cyberjack_open(struct tty_struct *tty,
1368c2ecf20Sopenharmony_ci					struct usb_serial_port *port)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct cyberjack_private *priv;
1398c2ecf20Sopenharmony_ci	unsigned long flags;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - usb_clear_halt\n", __func__);
1428c2ecf20Sopenharmony_ci	usb_clear_halt(port->serial->dev, port->write_urb->pipe);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
1458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
1468c2ecf20Sopenharmony_ci	priv->rdtodo = 0;
1478c2ecf20Sopenharmony_ci	priv->wrfilled = 0;
1488c2ecf20Sopenharmony_ci	priv->wrsent = 0;
1498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return 0;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void cyberjack_close(struct usb_serial_port *port)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	usb_kill_urb(port->write_urb);
1578c2ecf20Sopenharmony_ci	usb_kill_urb(port->read_urb);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic int cyberjack_write(struct tty_struct *tty,
1618c2ecf20Sopenharmony_ci	struct usb_serial_port *port, const unsigned char *buf, int count)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct device *dev = &port->dev;
1648c2ecf20Sopenharmony_ci	struct cyberjack_private *priv = usb_get_serial_port_data(port);
1658c2ecf20Sopenharmony_ci	unsigned long flags;
1668c2ecf20Sopenharmony_ci	int result;
1678c2ecf20Sopenharmony_ci	int wrexpected;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (count == 0) {
1708c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - write request of 0 bytes\n", __func__);
1718c2ecf20Sopenharmony_ci		return 0;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (!test_and_clear_bit(0, &port->write_urbs_free)) {
1758c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - already writing\n", __func__);
1768c2ecf20Sopenharmony_ci		return 0;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (count+priv->wrfilled > sizeof(priv->wrbuf)) {
1828c2ecf20Sopenharmony_ci		/* To much data for buffer. Reset buffer. */
1838c2ecf20Sopenharmony_ci		priv->wrfilled = 0;
1848c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
1858c2ecf20Sopenharmony_ci		set_bit(0, &port->write_urbs_free);
1868c2ecf20Sopenharmony_ci		return 0;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* Copy data */
1908c2ecf20Sopenharmony_ci	memcpy(priv->wrbuf + priv->wrfilled, buf, count);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	usb_serial_debug_data(dev, __func__, count, priv->wrbuf + priv->wrfilled);
1938c2ecf20Sopenharmony_ci	priv->wrfilled += count;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (priv->wrfilled >= 3) {
1968c2ecf20Sopenharmony_ci		wrexpected = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3;
1978c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - expected data: %d\n", __func__, wrexpected);
1988c2ecf20Sopenharmony_ci	} else
1998c2ecf20Sopenharmony_ci		wrexpected = sizeof(priv->wrbuf);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (priv->wrfilled >= wrexpected) {
2028c2ecf20Sopenharmony_ci		/* We have enough data to begin transmission */
2038c2ecf20Sopenharmony_ci		int length;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - transmitting data (frame 1)\n", __func__);
2068c2ecf20Sopenharmony_ci		length = (wrexpected > port->bulk_out_size) ?
2078c2ecf20Sopenharmony_ci					port->bulk_out_size : wrexpected;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		memcpy(port->write_urb->transfer_buffer, priv->wrbuf, length);
2108c2ecf20Sopenharmony_ci		priv->wrsent = length;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		/* set up our urb */
2138c2ecf20Sopenharmony_ci		port->write_urb->transfer_buffer_length = length;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		/* send the data out the bulk port */
2168c2ecf20Sopenharmony_ci		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
2178c2ecf20Sopenharmony_ci		if (result) {
2188c2ecf20Sopenharmony_ci			dev_err(&port->dev,
2198c2ecf20Sopenharmony_ci				"%s - failed submitting write urb, error %d\n",
2208c2ecf20Sopenharmony_ci				__func__, result);
2218c2ecf20Sopenharmony_ci			/* Throw away data. No better idea what to do with it. */
2228c2ecf20Sopenharmony_ci			priv->wrfilled = 0;
2238c2ecf20Sopenharmony_ci			priv->wrsent = 0;
2248c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&priv->lock, flags);
2258c2ecf20Sopenharmony_ci			set_bit(0, &port->write_urbs_free);
2268c2ecf20Sopenharmony_ci			return 0;
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - priv->wrsent=%d\n", __func__, priv->wrsent);
2308c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - priv->wrfilled=%d\n", __func__, priv->wrfilled);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		if (priv->wrsent >= priv->wrfilled) {
2338c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - buffer cleaned\n", __func__);
2348c2ecf20Sopenharmony_ci			memset(priv->wrbuf, 0, sizeof(priv->wrbuf));
2358c2ecf20Sopenharmony_ci			priv->wrfilled = 0;
2368c2ecf20Sopenharmony_ci			priv->wrsent = 0;
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return count;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic int cyberjack_write_room(struct tty_struct *tty)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	/* FIXME: .... */
2488c2ecf20Sopenharmony_ci	return CYBERJACK_LOCAL_BUF_SIZE;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic void cyberjack_read_int_callback(struct urb *urb)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
2548c2ecf20Sopenharmony_ci	struct cyberjack_private *priv = usb_get_serial_port_data(port);
2558c2ecf20Sopenharmony_ci	struct device *dev = &port->dev;
2568c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
2578c2ecf20Sopenharmony_ci	int status = urb->status;
2588c2ecf20Sopenharmony_ci	unsigned long flags;
2598c2ecf20Sopenharmony_ci	int result;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/* the urb might have been killed. */
2628c2ecf20Sopenharmony_ci	if (status)
2638c2ecf20Sopenharmony_ci		return;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	usb_serial_debug_data(dev, __func__, urb->actual_length, data);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/* React only to interrupts signaling a bulk_in transfer */
2688c2ecf20Sopenharmony_ci	if (urb->actual_length == 4 && data[0] == 0x01) {
2698c2ecf20Sopenharmony_ci		short old_rdtodo;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci		/* This is a announcement of coming bulk_ins. */
2728c2ecf20Sopenharmony_ci		unsigned short size = ((unsigned short)data[3]<<8)+data[2]+3;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		spin_lock_irqsave(&priv->lock, flags);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		old_rdtodo = priv->rdtodo;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		if (old_rdtodo > SHRT_MAX - size) {
2798c2ecf20Sopenharmony_ci			dev_dbg(dev, "Too many bulk_in urbs to do.\n");
2808c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&priv->lock, flags);
2818c2ecf20Sopenharmony_ci			goto resubmit;
2828c2ecf20Sopenharmony_ci		}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		/* "+=" is probably more fault tolerant than "=" */
2858c2ecf20Sopenharmony_ci		priv->rdtodo += size;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - rdtodo: %d\n", __func__, priv->rdtodo);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci		if (!old_rdtodo) {
2928c2ecf20Sopenharmony_ci			result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
2938c2ecf20Sopenharmony_ci			if (result)
2948c2ecf20Sopenharmony_ci				dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
2958c2ecf20Sopenharmony_ci					__func__, result);
2968c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - usb_submit_urb(read urb)\n", __func__);
2978c2ecf20Sopenharmony_ci		}
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ciresubmit:
3018c2ecf20Sopenharmony_ci	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
3028c2ecf20Sopenharmony_ci	if (result)
3038c2ecf20Sopenharmony_ci		dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
3048c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - usb_submit_urb(int urb)\n", __func__);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic void cyberjack_read_bulk_callback(struct urb *urb)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
3108c2ecf20Sopenharmony_ci	struct cyberjack_private *priv = usb_get_serial_port_data(port);
3118c2ecf20Sopenharmony_ci	struct device *dev = &port->dev;
3128c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
3138c2ecf20Sopenharmony_ci	unsigned long flags;
3148c2ecf20Sopenharmony_ci	short todo;
3158c2ecf20Sopenharmony_ci	int result;
3168c2ecf20Sopenharmony_ci	int status = urb->status;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	usb_serial_debug_data(dev, __func__, urb->actual_length, data);
3198c2ecf20Sopenharmony_ci	if (status) {
3208c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - nonzero read bulk status received: %d\n",
3218c2ecf20Sopenharmony_ci			__func__, status);
3228c2ecf20Sopenharmony_ci		return;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (urb->actual_length) {
3268c2ecf20Sopenharmony_ci		tty_insert_flip_string(&port->port, data, urb->actual_length);
3278c2ecf20Sopenharmony_ci		tty_flip_buffer_push(&port->port);
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* Reduce urbs to do by one. */
3338c2ecf20Sopenharmony_ci	priv->rdtodo -= urb->actual_length;
3348c2ecf20Sopenharmony_ci	/* Just to be sure */
3358c2ecf20Sopenharmony_ci	if (priv->rdtodo < 0)
3368c2ecf20Sopenharmony_ci		priv->rdtodo = 0;
3378c2ecf20Sopenharmony_ci	todo = priv->rdtodo;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - rdtodo: %d\n", __func__, todo);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* Continue to read if we have still urbs to do. */
3448c2ecf20Sopenharmony_ci	if (todo /* || (urb->actual_length==port->bulk_in_endpointAddress)*/) {
3458c2ecf20Sopenharmony_ci		result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
3468c2ecf20Sopenharmony_ci		if (result)
3478c2ecf20Sopenharmony_ci			dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
3488c2ecf20Sopenharmony_ci				__func__, result);
3498c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - usb_submit_urb(read urb)\n", __func__);
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic void cyberjack_write_bulk_callback(struct urb *urb)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
3568c2ecf20Sopenharmony_ci	struct cyberjack_private *priv = usb_get_serial_port_data(port);
3578c2ecf20Sopenharmony_ci	struct device *dev = &port->dev;
3588c2ecf20Sopenharmony_ci	int status = urb->status;
3598c2ecf20Sopenharmony_ci	unsigned long flags;
3608c2ecf20Sopenharmony_ci	bool resubmitted = false;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (status) {
3638c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - nonzero write bulk status received: %d\n",
3648c2ecf20Sopenharmony_ci			__func__, status);
3658c2ecf20Sopenharmony_ci		set_bit(0, &port->write_urbs_free);
3668c2ecf20Sopenharmony_ci		return;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* only do something if we have more data to send */
3728c2ecf20Sopenharmony_ci	if (priv->wrfilled) {
3738c2ecf20Sopenharmony_ci		int length, blksize, result;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - transmitting data (frame n)\n", __func__);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		length = ((priv->wrfilled - priv->wrsent) > port->bulk_out_size) ?
3788c2ecf20Sopenharmony_ci			port->bulk_out_size : (priv->wrfilled - priv->wrsent);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		memcpy(port->write_urb->transfer_buffer,
3818c2ecf20Sopenharmony_ci					priv->wrbuf + priv->wrsent, length);
3828c2ecf20Sopenharmony_ci		priv->wrsent += length;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		/* set up our urb */
3858c2ecf20Sopenharmony_ci		port->write_urb->transfer_buffer_length = length;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		/* send the data out the bulk port */
3888c2ecf20Sopenharmony_ci		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
3898c2ecf20Sopenharmony_ci		if (result) {
3908c2ecf20Sopenharmony_ci			dev_err(dev, "%s - failed submitting write urb, error %d\n",
3918c2ecf20Sopenharmony_ci				__func__, result);
3928c2ecf20Sopenharmony_ci			/* Throw away data. No better idea what to do with it. */
3938c2ecf20Sopenharmony_ci			priv->wrfilled = 0;
3948c2ecf20Sopenharmony_ci			priv->wrsent = 0;
3958c2ecf20Sopenharmony_ci			goto exit;
3968c2ecf20Sopenharmony_ci		}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci		resubmitted = true;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - priv->wrsent=%d\n", __func__, priv->wrsent);
4018c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - priv->wrfilled=%d\n", __func__, priv->wrfilled);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		blksize = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		if (priv->wrsent >= priv->wrfilled ||
4068c2ecf20Sopenharmony_ci					priv->wrsent >= blksize) {
4078c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - buffer cleaned\n", __func__);
4088c2ecf20Sopenharmony_ci			memset(priv->wrbuf, 0, sizeof(priv->wrbuf));
4098c2ecf20Sopenharmony_ci			priv->wrfilled = 0;
4108c2ecf20Sopenharmony_ci			priv->wrsent = 0;
4118c2ecf20Sopenharmony_ci		}
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ciexit:
4158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
4168c2ecf20Sopenharmony_ci	if (!resubmitted)
4178c2ecf20Sopenharmony_ci		set_bit(0, &port->write_urbs_free);
4188c2ecf20Sopenharmony_ci	usb_serial_port_softint(port);
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
4248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
4258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
426