18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * KOBIL USB Smart Card Terminal Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2002 KOBIL Systems GmbH 68c2ecf20Sopenharmony_ci * Author: Thomas Wahrenbruch 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Contact: linuxusb@kobil.de 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 * Supported readers: USB TWIN, KAAN Standard Plus and SecOVID Reader Plus 188c2ecf20Sopenharmony_ci * (Adapter K), B1 Professional and KAAN Professional (Adapter B) 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/errno.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/tty.h> 268c2ecf20Sopenharmony_ci#include <linux/tty_driver.h> 278c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 308c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 318c2ecf20Sopenharmony_ci#include <linux/usb.h> 328c2ecf20Sopenharmony_ci#include <linux/usb/serial.h> 338c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 348c2ecf20Sopenharmony_ci#include "kobil_sct.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "KOBIL Systems GmbH - http://www.kobil.com" 378c2ecf20Sopenharmony_ci#define DRIVER_DESC "KOBIL USB Smart Card Terminal Driver (experimental)" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define KOBIL_VENDOR_ID 0x0D46 408c2ecf20Sopenharmony_ci#define KOBIL_ADAPTER_B_PRODUCT_ID 0x2011 418c2ecf20Sopenharmony_ci#define KOBIL_ADAPTER_K_PRODUCT_ID 0x2012 428c2ecf20Sopenharmony_ci#define KOBIL_USBTWIN_PRODUCT_ID 0x0078 438c2ecf20Sopenharmony_ci#define KOBIL_KAAN_SIM_PRODUCT_ID 0x0081 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define KOBIL_TIMEOUT 500 468c2ecf20Sopenharmony_ci#define KOBIL_BUF_LENGTH 300 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* Function prototypes */ 508c2ecf20Sopenharmony_cistatic int kobil_port_probe(struct usb_serial_port *probe); 518c2ecf20Sopenharmony_cistatic int kobil_port_remove(struct usb_serial_port *probe); 528c2ecf20Sopenharmony_cistatic int kobil_open(struct tty_struct *tty, struct usb_serial_port *port); 538c2ecf20Sopenharmony_cistatic void kobil_close(struct usb_serial_port *port); 548c2ecf20Sopenharmony_cistatic int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, 558c2ecf20Sopenharmony_ci const unsigned char *buf, int count); 568c2ecf20Sopenharmony_cistatic int kobil_write_room(struct tty_struct *tty); 578c2ecf20Sopenharmony_cistatic int kobil_ioctl(struct tty_struct *tty, 588c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg); 598c2ecf20Sopenharmony_cistatic int kobil_tiocmget(struct tty_struct *tty); 608c2ecf20Sopenharmony_cistatic int kobil_tiocmset(struct tty_struct *tty, 618c2ecf20Sopenharmony_ci unsigned int set, unsigned int clear); 628c2ecf20Sopenharmony_cistatic void kobil_read_int_callback(struct urb *urb); 638c2ecf20Sopenharmony_cistatic void kobil_write_int_callback(struct urb *urb); 648c2ecf20Sopenharmony_cistatic void kobil_set_termios(struct tty_struct *tty, 658c2ecf20Sopenharmony_ci struct usb_serial_port *port, struct ktermios *old); 668c2ecf20Sopenharmony_cistatic void kobil_init_termios(struct tty_struct *tty); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = { 698c2ecf20Sopenharmony_ci { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_B_PRODUCT_ID) }, 708c2ecf20Sopenharmony_ci { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_K_PRODUCT_ID) }, 718c2ecf20Sopenharmony_ci { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_USBTWIN_PRODUCT_ID) }, 728c2ecf20Sopenharmony_ci { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_KAAN_SIM_PRODUCT_ID) }, 738c2ecf20Sopenharmony_ci { } /* Terminating entry */ 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic struct usb_serial_driver kobil_device = { 788c2ecf20Sopenharmony_ci .driver = { 798c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 808c2ecf20Sopenharmony_ci .name = "kobil", 818c2ecf20Sopenharmony_ci }, 828c2ecf20Sopenharmony_ci .description = "KOBIL USB smart card terminal", 838c2ecf20Sopenharmony_ci .id_table = id_table, 848c2ecf20Sopenharmony_ci .num_ports = 1, 858c2ecf20Sopenharmony_ci .num_interrupt_out = 1, 868c2ecf20Sopenharmony_ci .port_probe = kobil_port_probe, 878c2ecf20Sopenharmony_ci .port_remove = kobil_port_remove, 888c2ecf20Sopenharmony_ci .ioctl = kobil_ioctl, 898c2ecf20Sopenharmony_ci .set_termios = kobil_set_termios, 908c2ecf20Sopenharmony_ci .init_termios = kobil_init_termios, 918c2ecf20Sopenharmony_ci .tiocmget = kobil_tiocmget, 928c2ecf20Sopenharmony_ci .tiocmset = kobil_tiocmset, 938c2ecf20Sopenharmony_ci .open = kobil_open, 948c2ecf20Sopenharmony_ci .close = kobil_close, 958c2ecf20Sopenharmony_ci .write = kobil_write, 968c2ecf20Sopenharmony_ci .write_room = kobil_write_room, 978c2ecf20Sopenharmony_ci .read_int_callback = kobil_read_int_callback, 988c2ecf20Sopenharmony_ci .write_int_callback = kobil_write_int_callback, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 1028c2ecf20Sopenharmony_ci &kobil_device, NULL 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistruct kobil_private { 1068c2ecf20Sopenharmony_ci unsigned char buf[KOBIL_BUF_LENGTH]; /* buffer for the APDU to send */ 1078c2ecf20Sopenharmony_ci int filled; /* index of the last char in buf */ 1088c2ecf20Sopenharmony_ci int cur_pos; /* index of the next char to send in buf */ 1098c2ecf20Sopenharmony_ci __u16 device_type; 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int kobil_port_probe(struct usb_serial_port *port) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct usb_serial *serial = port->serial; 1168c2ecf20Sopenharmony_ci struct kobil_private *priv; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci priv = kmalloc(sizeof(struct kobil_private), GFP_KERNEL); 1198c2ecf20Sopenharmony_ci if (!priv) 1208c2ecf20Sopenharmony_ci return -ENOMEM; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci priv->filled = 0; 1238c2ecf20Sopenharmony_ci priv->cur_pos = 0; 1248c2ecf20Sopenharmony_ci priv->device_type = le16_to_cpu(serial->dev->descriptor.idProduct); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci switch (priv->device_type) { 1278c2ecf20Sopenharmony_ci case KOBIL_ADAPTER_B_PRODUCT_ID: 1288c2ecf20Sopenharmony_ci dev_dbg(&serial->dev->dev, "KOBIL B1 PRO / KAAN PRO detected\n"); 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci case KOBIL_ADAPTER_K_PRODUCT_ID: 1318c2ecf20Sopenharmony_ci dev_dbg(&serial->dev->dev, "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n"); 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci case KOBIL_USBTWIN_PRODUCT_ID: 1348c2ecf20Sopenharmony_ci dev_dbg(&serial->dev->dev, "KOBIL USBTWIN detected\n"); 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci case KOBIL_KAAN_SIM_PRODUCT_ID: 1378c2ecf20Sopenharmony_ci dev_dbg(&serial->dev->dev, "KOBIL KAAN SIM detected\n"); 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci usb_set_serial_port_data(port, priv); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int kobil_port_remove(struct usb_serial_port *port) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct kobil_private *priv; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci priv = usb_get_serial_port_data(port); 1518c2ecf20Sopenharmony_ci kfree(priv); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void kobil_init_termios(struct tty_struct *tty) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci /* Default to echo off and other sane device settings */ 1598c2ecf20Sopenharmony_ci tty->termios.c_lflag = 0; 1608c2ecf20Sopenharmony_ci tty->termios.c_iflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE); 1618c2ecf20Sopenharmony_ci tty->termios.c_iflag |= IGNBRK | IGNPAR | IXOFF; 1628c2ecf20Sopenharmony_ci /* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */ 1638c2ecf20Sopenharmony_ci tty->termios.c_oflag &= ~ONLCR; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct device *dev = &port->dev; 1698c2ecf20Sopenharmony_ci int result = 0; 1708c2ecf20Sopenharmony_ci struct kobil_private *priv; 1718c2ecf20Sopenharmony_ci unsigned char *transfer_buffer; 1728c2ecf20Sopenharmony_ci int transfer_buffer_length = 8; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci priv = usb_get_serial_port_data(port); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* allocate memory for transfer buffer */ 1778c2ecf20Sopenharmony_ci transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); 1788c2ecf20Sopenharmony_ci if (!transfer_buffer) 1798c2ecf20Sopenharmony_ci return -ENOMEM; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* get hardware version */ 1828c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 1838c2ecf20Sopenharmony_ci usb_rcvctrlpipe(port->serial->dev, 0), 1848c2ecf20Sopenharmony_ci SUSBCRequest_GetMisc, 1858c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, 1868c2ecf20Sopenharmony_ci SUSBCR_MSC_GetHWVersion, 1878c2ecf20Sopenharmony_ci 0, 1888c2ecf20Sopenharmony_ci transfer_buffer, 1898c2ecf20Sopenharmony_ci transfer_buffer_length, 1908c2ecf20Sopenharmony_ci KOBIL_TIMEOUT 1918c2ecf20Sopenharmony_ci ); 1928c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result); 1938c2ecf20Sopenharmony_ci if (result >= 3) { 1948c2ecf20Sopenharmony_ci dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0], 1958c2ecf20Sopenharmony_ci transfer_buffer[1], transfer_buffer[2]); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* get firmware version */ 1998c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 2008c2ecf20Sopenharmony_ci usb_rcvctrlpipe(port->serial->dev, 0), 2018c2ecf20Sopenharmony_ci SUSBCRequest_GetMisc, 2028c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, 2038c2ecf20Sopenharmony_ci SUSBCR_MSC_GetFWVersion, 2048c2ecf20Sopenharmony_ci 0, 2058c2ecf20Sopenharmony_ci transfer_buffer, 2068c2ecf20Sopenharmony_ci transfer_buffer_length, 2078c2ecf20Sopenharmony_ci KOBIL_TIMEOUT 2088c2ecf20Sopenharmony_ci ); 2098c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result); 2108c2ecf20Sopenharmony_ci if (result >= 3) { 2118c2ecf20Sopenharmony_ci dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0], 2128c2ecf20Sopenharmony_ci transfer_buffer[1], transfer_buffer[2]); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || 2168c2ecf20Sopenharmony_ci priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { 2178c2ecf20Sopenharmony_ci /* Setting Baudrate, Parity and Stopbits */ 2188c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 2198c2ecf20Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 2208c2ecf20Sopenharmony_ci SUSBCRequest_SetBaudRateParityAndStopBits, 2218c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, 2228c2ecf20Sopenharmony_ci SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity | 2238c2ecf20Sopenharmony_ci SUSBCR_SPASB_1StopBit, 2248c2ecf20Sopenharmony_ci 0, 2258c2ecf20Sopenharmony_ci NULL, 2268c2ecf20Sopenharmony_ci 0, 2278c2ecf20Sopenharmony_ci KOBIL_TIMEOUT 2288c2ecf20Sopenharmony_ci ); 2298c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - Send set_baudrate URB returns: %i\n", __func__, result); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* reset all queues */ 2328c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 2338c2ecf20Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 2348c2ecf20Sopenharmony_ci SUSBCRequest_Misc, 2358c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, 2368c2ecf20Sopenharmony_ci SUSBCR_MSC_ResetAllQueues, 2378c2ecf20Sopenharmony_ci 0, 2388c2ecf20Sopenharmony_ci NULL, 2398c2ecf20Sopenharmony_ci 0, 2408c2ecf20Sopenharmony_ci KOBIL_TIMEOUT 2418c2ecf20Sopenharmony_ci ); 2428c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || 2458c2ecf20Sopenharmony_ci priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || 2468c2ecf20Sopenharmony_ci priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { 2478c2ecf20Sopenharmony_ci /* start reading (Adapter B 'cause PNP string) */ 2488c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 2498c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - Send read URB returns: %i\n", __func__, result); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci kfree(transfer_buffer); 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void kobil_close(struct usb_serial_port *port) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci /* FIXME: Add rts/dtr methods */ 2608c2ecf20Sopenharmony_ci usb_kill_urb(port->interrupt_out_urb); 2618c2ecf20Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic void kobil_read_int_callback(struct urb *urb) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci int result; 2688c2ecf20Sopenharmony_ci struct usb_serial_port *port = urb->context; 2698c2ecf20Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 2708c2ecf20Sopenharmony_ci int status = urb->status; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (status) { 2738c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - Read int status not zero: %d\n", __func__, status); 2748c2ecf20Sopenharmony_ci return; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (urb->actual_length) { 2788c2ecf20Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, urb->actual_length, 2798c2ecf20Sopenharmony_ci data); 2808c2ecf20Sopenharmony_ci tty_insert_flip_string(&port->port, data, urb->actual_length); 2818c2ecf20Sopenharmony_ci tty_flip_buffer_push(&port->port); 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); 2858c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic void kobil_write_int_callback(struct urb *urb) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, 2958c2ecf20Sopenharmony_ci const unsigned char *buf, int count) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci int length = 0; 2988c2ecf20Sopenharmony_ci int result = 0; 2998c2ecf20Sopenharmony_ci int todo = 0; 3008c2ecf20Sopenharmony_ci struct kobil_private *priv; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (count == 0) { 3038c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__); 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci priv = usb_get_serial_port_data(port); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (count > (KOBIL_BUF_LENGTH - priv->filled)) { 3108c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - Error: write request bigger than buffer size\n", __func__); 3118c2ecf20Sopenharmony_ci return -ENOMEM; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Copy data to buffer */ 3158c2ecf20Sopenharmony_ci memcpy(priv->buf + priv->filled, buf, count); 3168c2ecf20Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, count, priv->buf + priv->filled); 3178c2ecf20Sopenharmony_ci priv->filled = priv->filled + count; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* only send complete block. TWIN, KAAN SIM and adapter K 3208c2ecf20Sopenharmony_ci use the same protocol. */ 3218c2ecf20Sopenharmony_ci if (((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) || 3228c2ecf20Sopenharmony_ci ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4)))) { 3238c2ecf20Sopenharmony_ci /* stop reading (except TWIN and KAAN SIM) */ 3248c2ecf20Sopenharmony_ci if ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) 3258c2ecf20Sopenharmony_ci || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID)) 3268c2ecf20Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci todo = priv->filled - priv->cur_pos; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci while (todo > 0) { 3318c2ecf20Sopenharmony_ci /* max 8 byte in one urb (endpoint size) */ 3328c2ecf20Sopenharmony_ci length = min(todo, port->interrupt_out_size); 3338c2ecf20Sopenharmony_ci /* copy data to transfer buffer */ 3348c2ecf20Sopenharmony_ci memcpy(port->interrupt_out_buffer, 3358c2ecf20Sopenharmony_ci priv->buf + priv->cur_pos, length); 3368c2ecf20Sopenharmony_ci port->interrupt_out_urb->transfer_buffer_length = length; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci priv->cur_pos = priv->cur_pos + length; 3398c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_out_urb, 3408c2ecf20Sopenharmony_ci GFP_ATOMIC); 3418c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - Send write URB returns: %i\n", __func__, result); 3428c2ecf20Sopenharmony_ci todo = priv->filled - priv->cur_pos; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (todo > 0) 3458c2ecf20Sopenharmony_ci msleep(24); 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci priv->filled = 0; 3498c2ecf20Sopenharmony_ci priv->cur_pos = 0; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* start reading (except TWIN and KAAN SIM) */ 3528c2ecf20Sopenharmony_ci if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || 3538c2ecf20Sopenharmony_ci priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { 3548c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, 3558c2ecf20Sopenharmony_ci GFP_ATOMIC); 3568c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci return count; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int kobil_write_room(struct tty_struct *tty) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci /* FIXME */ 3668c2ecf20Sopenharmony_ci return 8; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int kobil_tiocmget(struct tty_struct *tty) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 3738c2ecf20Sopenharmony_ci struct kobil_private *priv; 3748c2ecf20Sopenharmony_ci int result; 3758c2ecf20Sopenharmony_ci unsigned char *transfer_buffer; 3768c2ecf20Sopenharmony_ci int transfer_buffer_length = 8; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci priv = usb_get_serial_port_data(port); 3798c2ecf20Sopenharmony_ci if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID 3808c2ecf20Sopenharmony_ci || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { 3818c2ecf20Sopenharmony_ci /* This device doesn't support ioctl calls */ 3828c2ecf20Sopenharmony_ci return -EINVAL; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* allocate memory for transfer buffer */ 3868c2ecf20Sopenharmony_ci transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); 3878c2ecf20Sopenharmony_ci if (!transfer_buffer) 3888c2ecf20Sopenharmony_ci return -ENOMEM; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 3918c2ecf20Sopenharmony_ci usb_rcvctrlpipe(port->serial->dev, 0), 3928c2ecf20Sopenharmony_ci SUSBCRequest_GetStatusLineState, 3938c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, 3948c2ecf20Sopenharmony_ci 0, 3958c2ecf20Sopenharmony_ci 0, 3968c2ecf20Sopenharmony_ci transfer_buffer, 3978c2ecf20Sopenharmony_ci transfer_buffer_length, 3988c2ecf20Sopenharmony_ci KOBIL_TIMEOUT); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Send get_status_line_state URB returns: %i\n", 4018c2ecf20Sopenharmony_ci result); 4028c2ecf20Sopenharmony_ci if (result < 1) { 4038c2ecf20Sopenharmony_ci if (result >= 0) 4048c2ecf20Sopenharmony_ci result = -EIO; 4058c2ecf20Sopenharmony_ci goto out_free; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Statusline: %02x\n", transfer_buffer[0]); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci result = 0; 4118c2ecf20Sopenharmony_ci if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0) 4128c2ecf20Sopenharmony_ci result = TIOCM_DSR; 4138c2ecf20Sopenharmony_ciout_free: 4148c2ecf20Sopenharmony_ci kfree(transfer_buffer); 4158c2ecf20Sopenharmony_ci return result; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic int kobil_tiocmset(struct tty_struct *tty, 4198c2ecf20Sopenharmony_ci unsigned int set, unsigned int clear) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 4228c2ecf20Sopenharmony_ci struct device *dev = &port->dev; 4238c2ecf20Sopenharmony_ci struct kobil_private *priv; 4248c2ecf20Sopenharmony_ci int result; 4258c2ecf20Sopenharmony_ci int dtr = 0; 4268c2ecf20Sopenharmony_ci int rts = 0; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* FIXME: locking ? */ 4298c2ecf20Sopenharmony_ci priv = usb_get_serial_port_data(port); 4308c2ecf20Sopenharmony_ci if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID 4318c2ecf20Sopenharmony_ci || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { 4328c2ecf20Sopenharmony_ci /* This device doesn't support ioctl calls */ 4338c2ecf20Sopenharmony_ci return -EINVAL; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (set & TIOCM_RTS) 4378c2ecf20Sopenharmony_ci rts = 1; 4388c2ecf20Sopenharmony_ci if (set & TIOCM_DTR) 4398c2ecf20Sopenharmony_ci dtr = 1; 4408c2ecf20Sopenharmony_ci if (clear & TIOCM_RTS) 4418c2ecf20Sopenharmony_ci rts = 0; 4428c2ecf20Sopenharmony_ci if (clear & TIOCM_DTR) 4438c2ecf20Sopenharmony_ci dtr = 0; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) { 4468c2ecf20Sopenharmony_ci if (dtr != 0) 4478c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - Setting DTR\n", __func__); 4488c2ecf20Sopenharmony_ci else 4498c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - Clearing DTR\n", __func__); 4508c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 4518c2ecf20Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 4528c2ecf20Sopenharmony_ci SUSBCRequest_SetStatusLinesOrQueues, 4538c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, 4548c2ecf20Sopenharmony_ci ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR), 4558c2ecf20Sopenharmony_ci 0, 4568c2ecf20Sopenharmony_ci NULL, 4578c2ecf20Sopenharmony_ci 0, 4588c2ecf20Sopenharmony_ci KOBIL_TIMEOUT); 4598c2ecf20Sopenharmony_ci } else { 4608c2ecf20Sopenharmony_ci if (rts != 0) 4618c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - Setting RTS\n", __func__); 4628c2ecf20Sopenharmony_ci else 4638c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - Clearing RTS\n", __func__); 4648c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 4658c2ecf20Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 4668c2ecf20Sopenharmony_ci SUSBCRequest_SetStatusLinesOrQueues, 4678c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, 4688c2ecf20Sopenharmony_ci ((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS), 4698c2ecf20Sopenharmony_ci 0, 4708c2ecf20Sopenharmony_ci NULL, 4718c2ecf20Sopenharmony_ci 0, 4728c2ecf20Sopenharmony_ci KOBIL_TIMEOUT); 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - Send set_status_line URB returns: %i\n", __func__, result); 4758c2ecf20Sopenharmony_ci return (result < 0) ? result : 0; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic void kobil_set_termios(struct tty_struct *tty, 4798c2ecf20Sopenharmony_ci struct usb_serial_port *port, struct ktermios *old) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct kobil_private *priv; 4828c2ecf20Sopenharmony_ci int result; 4838c2ecf20Sopenharmony_ci unsigned short urb_val = 0; 4848c2ecf20Sopenharmony_ci int c_cflag = tty->termios.c_cflag; 4858c2ecf20Sopenharmony_ci speed_t speed; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci priv = usb_get_serial_port_data(port); 4888c2ecf20Sopenharmony_ci if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || 4898c2ecf20Sopenharmony_ci priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { 4908c2ecf20Sopenharmony_ci /* This device doesn't support ioctl calls */ 4918c2ecf20Sopenharmony_ci tty_termios_copy_hw(&tty->termios, old); 4928c2ecf20Sopenharmony_ci return; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci speed = tty_get_baud_rate(tty); 4968c2ecf20Sopenharmony_ci switch (speed) { 4978c2ecf20Sopenharmony_ci case 1200: 4988c2ecf20Sopenharmony_ci urb_val = SUSBCR_SBR_1200; 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci default: 5018c2ecf20Sopenharmony_ci speed = 9600; 5028c2ecf20Sopenharmony_ci fallthrough; 5038c2ecf20Sopenharmony_ci case 9600: 5048c2ecf20Sopenharmony_ci urb_val = SUSBCR_SBR_9600; 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits : 5088c2ecf20Sopenharmony_ci SUSBCR_SPASB_1StopBit; 5098c2ecf20Sopenharmony_ci if (c_cflag & PARENB) { 5108c2ecf20Sopenharmony_ci if (c_cflag & PARODD) 5118c2ecf20Sopenharmony_ci urb_val |= SUSBCR_SPASB_OddParity; 5128c2ecf20Sopenharmony_ci else 5138c2ecf20Sopenharmony_ci urb_val |= SUSBCR_SPASB_EvenParity; 5148c2ecf20Sopenharmony_ci } else 5158c2ecf20Sopenharmony_ci urb_val |= SUSBCR_SPASB_NoParity; 5168c2ecf20Sopenharmony_ci tty->termios.c_cflag &= ~CMSPAR; 5178c2ecf20Sopenharmony_ci tty_encode_baud_rate(tty, speed, speed); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 5208c2ecf20Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 5218c2ecf20Sopenharmony_ci SUSBCRequest_SetBaudRateParityAndStopBits, 5228c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, 5238c2ecf20Sopenharmony_ci urb_val, 5248c2ecf20Sopenharmony_ci 0, 5258c2ecf20Sopenharmony_ci NULL, 5268c2ecf20Sopenharmony_ci 0, 5278c2ecf20Sopenharmony_ci KOBIL_TIMEOUT 5288c2ecf20Sopenharmony_ci ); 5298c2ecf20Sopenharmony_ci if (result) { 5308c2ecf20Sopenharmony_ci dev_err(&port->dev, "failed to update line settings: %d\n", 5318c2ecf20Sopenharmony_ci result); 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int kobil_ioctl(struct tty_struct *tty, 5368c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 5398c2ecf20Sopenharmony_ci struct kobil_private *priv = usb_get_serial_port_data(port); 5408c2ecf20Sopenharmony_ci int result; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || 5438c2ecf20Sopenharmony_ci priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) 5448c2ecf20Sopenharmony_ci /* This device doesn't support ioctl calls */ 5458c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci switch (cmd) { 5488c2ecf20Sopenharmony_ci case TCFLSH: 5498c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 5508c2ecf20Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 5518c2ecf20Sopenharmony_ci SUSBCRequest_Misc, 5528c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, 5538c2ecf20Sopenharmony_ci SUSBCR_MSC_ResetAllQueues, 5548c2ecf20Sopenharmony_ci 0, 5558c2ecf20Sopenharmony_ci NULL, 5568c2ecf20Sopenharmony_ci 0, 5578c2ecf20Sopenharmony_ci KOBIL_TIMEOUT 5588c2ecf20Sopenharmony_ci ); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci dev_dbg(&port->dev, 5618c2ecf20Sopenharmony_ci "%s - Send reset_all_queues (FLUSH) URB returns: %i\n", 5628c2ecf20Sopenharmony_ci __func__, result); 5638c2ecf20Sopenharmony_ci return (result < 0) ? -EIO: 0; 5648c2ecf20Sopenharmony_ci default: 5658c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 5728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 5738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 574