162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * USB Debug cable driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006 Greg Kroah-Hartman <greg@kroah.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/gfp.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/tty.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/usb.h>
1362306a36Sopenharmony_ci#include <linux/usb/serial.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define USB_DEBUG_MAX_PACKET_SIZE	8
1662306a36Sopenharmony_ci#define USB_DEBUG_BRK_SIZE		8
1762306a36Sopenharmony_cistatic const char USB_DEBUG_BRK[USB_DEBUG_BRK_SIZE] = {
1862306a36Sopenharmony_ci	0x00,
1962306a36Sopenharmony_ci	0xff,
2062306a36Sopenharmony_ci	0x01,
2162306a36Sopenharmony_ci	0xfe,
2262306a36Sopenharmony_ci	0x00,
2362306a36Sopenharmony_ci	0xfe,
2462306a36Sopenharmony_ci	0x01,
2562306a36Sopenharmony_ci	0xff,
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = {
2962306a36Sopenharmony_ci	{ USB_DEVICE(0x0525, 0x127a) },
3062306a36Sopenharmony_ci	{ },
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const struct usb_device_id dbc_id_table[] = {
3462306a36Sopenharmony_ci	{ USB_DEVICE(0x1d6b, 0x0010) },
3562306a36Sopenharmony_ci	{ USB_DEVICE(0x1d6b, 0x0011) },
3662306a36Sopenharmony_ci	{ },
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic const struct usb_device_id id_table_combined[] = {
4062306a36Sopenharmony_ci	{ USB_DEVICE(0x0525, 0x127a) },
4162306a36Sopenharmony_ci	{ USB_DEVICE(0x1d6b, 0x0010) },
4262306a36Sopenharmony_ci	{ USB_DEVICE(0x1d6b, 0x0011) },
4362306a36Sopenharmony_ci	{ },
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table_combined);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* This HW really does not support a serial break, so one will be
4862306a36Sopenharmony_ci * emulated when ever the break state is set to true.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistatic int usb_debug_break_ctl(struct tty_struct *tty, int break_state)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
5362306a36Sopenharmony_ci	int ret;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (!break_state)
5662306a36Sopenharmony_ci		return 0;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	ret = usb_serial_generic_write(tty, port, USB_DEBUG_BRK, USB_DEBUG_BRK_SIZE);
5962306a36Sopenharmony_ci	if (ret < 0)
6062306a36Sopenharmony_ci		return ret;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return 0;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void usb_debug_process_read_urb(struct urb *urb)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct usb_serial_port *port = urb->context;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (urb->actual_length == USB_DEBUG_BRK_SIZE &&
7062306a36Sopenharmony_ci		memcmp(urb->transfer_buffer, USB_DEBUG_BRK,
7162306a36Sopenharmony_ci						USB_DEBUG_BRK_SIZE) == 0) {
7262306a36Sopenharmony_ci		usb_serial_handle_break(port);
7362306a36Sopenharmony_ci		return;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	usb_serial_generic_process_read_urb(urb);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic struct usb_serial_driver debug_device = {
8062306a36Sopenharmony_ci	.driver = {
8162306a36Sopenharmony_ci		.owner =	THIS_MODULE,
8262306a36Sopenharmony_ci		.name =		"debug",
8362306a36Sopenharmony_ci	},
8462306a36Sopenharmony_ci	.id_table =		id_table,
8562306a36Sopenharmony_ci	.num_ports =		1,
8662306a36Sopenharmony_ci	.bulk_out_size =	USB_DEBUG_MAX_PACKET_SIZE,
8762306a36Sopenharmony_ci	.break_ctl =		usb_debug_break_ctl,
8862306a36Sopenharmony_ci	.process_read_urb =	usb_debug_process_read_urb,
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic struct usb_serial_driver dbc_device = {
9262306a36Sopenharmony_ci	.driver = {
9362306a36Sopenharmony_ci		.owner =	THIS_MODULE,
9462306a36Sopenharmony_ci		.name =		"xhci_dbc",
9562306a36Sopenharmony_ci	},
9662306a36Sopenharmony_ci	.id_table =		dbc_id_table,
9762306a36Sopenharmony_ci	.num_ports =		1,
9862306a36Sopenharmony_ci	.break_ctl =		usb_debug_break_ctl,
9962306a36Sopenharmony_ci	.process_read_urb =	usb_debug_process_read_urb,
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
10362306a36Sopenharmony_ci	&debug_device, &dbc_device, NULL
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table_combined);
10762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
108