11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Safe Encapsulated USB Serial Driver 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 4241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold * Copyright (C) 2010 Johan Hovold <jhovold@gmail.com> 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2001 Lineo 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2001 Hewlett-Packard 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version. 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * By: 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Stuart Lynne <sl@lineo.com>, Tom Rushworth <tbr@lineo.com> 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1743c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox/* 1843c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * The encapsultaion is designed to overcome difficulties with some USB 1943c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * hardware. 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * While the USB protocol has a CRC over the data while in transit, i.e. while 2243c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * being carried over the bus, there is no end to end protection. If the 2343c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * hardware has any problems getting the data into or out of the USB transmit 2443c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * and receive FIFO's then data can be lost. 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 2643c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * This protocol adds a two byte trailer to each USB packet to specify the 2743c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * number of bytes of valid data and a 10 bit CRC that will allow the receiver 2843c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * to verify that the entire USB packet was received without error. 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 3043c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * Because in this case the sender and receiver are the class and function 3143c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * drivers there is now end to end protection. 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 3343c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * There is an additional option that can be used to force all transmitted 3443c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * packets to be padded to the maximum packet size. This provides a work 3543c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * around for some devices which have problems with small USB packets. 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Assuming a packetsize of N: 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 0..N-2 data and optional padding 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * N-2 bits 7-2 - number of bytes of valid data 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * bits 1-0 top two bits of 10 bit CRC 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * N-1 bottom 8 bits of 10 bit CRC 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * | Data Length | 10 bit CRC | 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * + 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 | 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 + 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 4943c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * The 10 bit CRC is computed across the sent data, followed by the trailer 5043c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * with the length set and the CRC set to zero. The CRC is then OR'd into 5143c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * the trailer. 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 5343c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * When received a 10 bit CRC is computed over the entire frame including 5443c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox * the trailer and should be equal to zero. 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Two module parameters are used to control the encapsulation, if both are 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * turned of the module works as a simple serial device with NO 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * encapsulation. 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * See linux/drivers/usbd/serial_fd for a device function driver 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * implementation of this. 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 685a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h> 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h> 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h> 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h> 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h> 7543c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox#include <linux/uaccess.h> 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h> 77a969888ce91673c7f4b86520d851a6f0d5a5fa7dGreg Kroah-Hartman#include <linux/usb/serial.h> 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 803aec6e26d7655eea07be8bbec4728447274ed43fRobert P. J. Day#ifndef CONFIG_USB_SERIAL_SAFE_PADDED 813aec6e26d7655eea07be8bbec4728447274ed43fRobert P. J. Day#define CONFIG_USB_SERIAL_SAFE_PADDED 0 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8490ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool debug; 8590ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool safe = 1; 8690ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool padded = CONFIG_USB_SERIAL_SAFE_PADDED; 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 88241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold#define DRIVER_VERSION "v0.1" 89241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold#define DRIVER_AUTHOR "sl@lineo.com, tbr@lineo.com, Johan Hovold <jhovold@gmail.com>" 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Safe Encapsulated Serial" 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9243c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan CoxMODULE_AUTHOR(DRIVER_AUTHOR); 9343c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan CoxMODULE_DESCRIPTION(DRIVER_DESC); 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9643c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Coxstatic __u16 vendor; /* no default */ 9743c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Coxstatic __u16 product; /* no default */ 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(vendor, ushort, 0); 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(vendor, "User specified USB idVendor (required)"); 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(product, ushort, 0); 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(product, "User specified USB idProduct (required)"); 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(debug, bool, S_IRUGO | S_IWUSR); 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(debug, "Debug enabled or not"); 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(safe, bool, 0); 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(safe, "Turn Safe Encapsulation On/Off"); 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(padded, bool, 0); 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(padded, "Pad to full wMaxPacketSize On/Off"); 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CDC_DEVICE_CLASS 0x02 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CDC_INTERFACE_CLASS 0x02 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CDC_INTERFACE_SUBCLASS 0x06 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_INTERFACE_CLASS 0xff 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_INTERFACE_SUBCLASS_SAFENET 0x01 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_SAFENET_CRC 0x01 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_SAFENET_CRC_PADDED 0x02 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_INTERFACE_SUBCLASS_SAFESERIAL 0x02 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_SAFESERIAL_CRC 0x01 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_SAFESERIAL_CRC_PADDED 0x02 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12843c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox#define MY_USB_DEVICE(vend, prod, dc, ic, isc) \ 12943c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ 13043c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox USB_DEVICE_ID_MATCH_DEV_CLASS | \ 13143c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox USB_DEVICE_ID_MATCH_INT_CLASS | \ 13243c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox USB_DEVICE_ID_MATCH_INT_SUBCLASS, \ 13343c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox .idVendor = (vend), \ 13443c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox .idProduct = (prod),\ 13543c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox .bDeviceClass = (dc),\ 13643c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox .bInterfaceClass = (ic), \ 13743c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox .bInterfaceSubClass = (isc), 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id id_table[] = { 14043c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox {MY_USB_DEVICE(0x49f, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Itsy */ 14143c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox {MY_USB_DEVICE(0x3f0, 0x2101, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Calypso */ 14243c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox {MY_USB_DEVICE(0x4dd, 0x8001, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Iris */ 14343c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox {MY_USB_DEVICE(0x4dd, 0x8002, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Collie */ 14443c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox {MY_USB_DEVICE(0x4dd, 0x8003, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Collie */ 14543c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox {MY_USB_DEVICE(0x4dd, 0x8004, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Collie */ 14643c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox {MY_USB_DEVICE(0x5f9, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Sharp tmp */ 14743c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox /* extra null entry for module vendor/produc parameters */ 14843c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox {MY_USB_DEVICE(0, 0, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, 14943c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox {} /* terminating entry */ 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15243c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan CoxMODULE_DEVICE_TABLE(usb, id_table); 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver safe_driver = { 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "safe_serial", 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .probe = usb_serial_probe, 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .disconnect = usb_serial_disconnect, 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .id_table = id_table, 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1614c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u16 crc10_table[256] = { 16243c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x000, 0x233, 0x255, 0x066, 0x299, 0x0aa, 0x0cc, 0x2ff, 16343c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x301, 0x132, 0x154, 0x367, 0x198, 0x3ab, 0x3cd, 0x1fe, 16443c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x031, 0x202, 0x264, 0x057, 0x2a8, 0x09b, 0x0fd, 0x2ce, 16543c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x330, 0x103, 0x165, 0x356, 0x1a9, 0x39a, 0x3fc, 0x1cf, 16643c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x062, 0x251, 0x237, 0x004, 0x2fb, 0x0c8, 0x0ae, 0x29d, 16743c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x363, 0x150, 0x136, 0x305, 0x1fa, 0x3c9, 0x3af, 0x19c, 16843c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x053, 0x260, 0x206, 0x035, 0x2ca, 0x0f9, 0x09f, 0x2ac, 16943c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x352, 0x161, 0x107, 0x334, 0x1cb, 0x3f8, 0x39e, 0x1ad, 17043c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x0c4, 0x2f7, 0x291, 0x0a2, 0x25d, 0x06e, 0x008, 0x23b, 17143c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x3c5, 0x1f6, 0x190, 0x3a3, 0x15c, 0x36f, 0x309, 0x13a, 17243c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x0f5, 0x2c6, 0x2a0, 0x093, 0x26c, 0x05f, 0x039, 0x20a, 17343c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x3f4, 0x1c7, 0x1a1, 0x392, 0x16d, 0x35e, 0x338, 0x10b, 17443c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x0a6, 0x295, 0x2f3, 0x0c0, 0x23f, 0x00c, 0x06a, 0x259, 17543c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x3a7, 0x194, 0x1f2, 0x3c1, 0x13e, 0x30d, 0x36b, 0x158, 17643c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x097, 0x2a4, 0x2c2, 0x0f1, 0x20e, 0x03d, 0x05b, 0x268, 17743c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x396, 0x1a5, 0x1c3, 0x3f0, 0x10f, 0x33c, 0x35a, 0x169, 17843c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x188, 0x3bb, 0x3dd, 0x1ee, 0x311, 0x122, 0x144, 0x377, 17943c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x289, 0x0ba, 0x0dc, 0x2ef, 0x010, 0x223, 0x245, 0x076, 18043c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x1b9, 0x38a, 0x3ec, 0x1df, 0x320, 0x113, 0x175, 0x346, 18143c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x2b8, 0x08b, 0x0ed, 0x2de, 0x021, 0x212, 0x274, 0x047, 18243c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x1ea, 0x3d9, 0x3bf, 0x18c, 0x373, 0x140, 0x126, 0x315, 18343c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x2eb, 0x0d8, 0x0be, 0x28d, 0x072, 0x241, 0x227, 0x014, 18443c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x1db, 0x3e8, 0x38e, 0x1bd, 0x342, 0x171, 0x117, 0x324, 18543c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x2da, 0x0e9, 0x08f, 0x2bc, 0x043, 0x270, 0x216, 0x025, 18643c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x14c, 0x37f, 0x319, 0x12a, 0x3d5, 0x1e6, 0x180, 0x3b3, 18743c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x24d, 0x07e, 0x018, 0x22b, 0x0d4, 0x2e7, 0x281, 0x0b2, 18843c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x17d, 0x34e, 0x328, 0x11b, 0x3e4, 0x1d7, 0x1b1, 0x382, 18943c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x27c, 0x04f, 0x029, 0x21a, 0x0e5, 0x2d6, 0x2b0, 0x083, 19043c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x12e, 0x31d, 0x37b, 0x148, 0x3b7, 0x184, 0x1e2, 0x3d1, 19143c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x22f, 0x01c, 0x07a, 0x249, 0x0b6, 0x285, 0x2e3, 0x0d0, 19243c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x11f, 0x32c, 0x34a, 0x179, 0x386, 0x1b5, 0x1d3, 0x3e0, 19343c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox 0x21e, 0x02d, 0x04b, 0x278, 0x087, 0x2b4, 0x2d2, 0x0e1, 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 19643c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox#define CRC10_INITFCS 0x000 /* Initial FCS value */ 19743c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox#define CRC10_GOODFCS 0x000 /* Good final FCS value */ 19843c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox#define CRC10_FCS(fcs, c) ((((fcs) << 8) & 0x3ff) ^ crc10_table[((fcs) >> 2) & 0xff] ^ (c)) 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 20043c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox/** 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * fcs_compute10 - memcpy and calculate 10 bit CRC across buffer 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @sp: pointer to buffer 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @len: number of bytes 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @fcs: starting FCS 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Perform a memcpy and calculate fcs using ppp 10bit CRC algorithm. Return 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * new 10 bit FCS. 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 20943c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Coxstatic __u16 __inline__ fcs_compute10(unsigned char *sp, int len, __u16 fcs) 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 21143c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox for (; len-- > 0; fcs = CRC10_FCS(fcs, *sp++)); 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return fcs; 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 21512e2e52cc578714d5824a27dd1a131a5418d636bJohan Hovoldstatic void safe_process_read_urb(struct urb *urb) 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 21712e2e52cc578714d5824a27dd1a131a5418d636bJohan Hovold struct usb_serial_port *port = urb->context; 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char *data = urb->transfer_buffer; 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char length = urb->actual_length; 2206d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold int actual_length; 2214a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox struct tty_struct *tty; 2226d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold __u16 fcs; 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 22412e2e52cc578714d5824a27dd1a131a5418d636bJohan Hovold if (!length) 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2274a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox tty = tty_port_tty_get(&port->port); 22812e2e52cc578714d5824a27dd1a131a5418d636bJohan Hovold if (!tty) 22912e2e52cc578714d5824a27dd1a131a5418d636bJohan Hovold return; 23012e2e52cc578714d5824a27dd1a131a5418d636bJohan Hovold 2316d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold if (!safe) 2326d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold goto out; 2336d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold 2346d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold fcs = fcs_compute10(data, length, CRC10_INITFCS); 2356d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold if (fcs) { 2366d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs); 2376d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold goto err; 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2406d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold actual_length = data[length - 2] >> 2; 2416d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold if (actual_length > (length - 2)) { 2426d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold dev_err(&port->dev, "%s - inconsistent lengths %d:%d\n", 2436d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold __func__, actual_length, length); 2446d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold goto err; 2456d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold } 2466d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold dev_info(&urb->dev->dev, "%s - actual: %d\n", __func__, actual_length); 2476d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold length = actual_length; 2486d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovoldout: 2496d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold tty_insert_flip_string(tty, data, length); 2506d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovold tty_flip_buffer_push(tty); 2516d1bf48e240bde4e9c7313ccdd2fe32f37f67ad4Johan Hovolderr: 25212e2e52cc578714d5824a27dd1a131a5418d636bJohan Hovold tty_kref_put(tty); 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 255241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovoldstatic int safe_prepare_write_buffer(struct usb_serial_port *port, 256241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold void *dest, size_t size) 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 258241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold unsigned char *buf = dest; 259241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold int count; 260241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold int trailer_len; 261241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold int pkt_len; 262241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold __u16 fcs; 263241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold 264241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold trailer_len = safe ? 2 : 0; 265241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold 266241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold count = kfifo_out_locked(&port->write_fifo, buf, size - trailer_len, 267241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold &port->lock); 268241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold if (!safe) 269241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold return count; 270241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold 271241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold /* pad if necessary */ 272241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold if (padded) { 273241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold pkt_len = size; 274241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold memset(buf + count, '0', pkt_len - count - trailer_len); 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 276241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold pkt_len = count + trailer_len; 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 279241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold /* set count */ 280241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold buf[pkt_len - 2] = count << 2; 281241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold buf[pkt_len - 1] = 0; 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 283241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold /* compute fcs and insert into trailer */ 284241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold fcs = fcs_compute10(buf, pkt_len, CRC10_INITFCS); 285241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold buf[pkt_len - 2] |= fcs >> 8; 286241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold buf[pkt_len - 1] |= fcs & 0xff; 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 288241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold return pkt_len; 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 29143c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Coxstatic int safe_startup(struct usb_serial *serial) 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (serial->interface->cur_altsetting->desc.bInterfaceProtocol) { 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case LINEO_SAFESERIAL_CRC: 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case LINEO_SAFESERIAL_CRC_PADDED: 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds padded = 1; 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 305ea65370d025f5005649e5cb37c4d025e92c6fc38Greg Kroah-Hartmanstatic struct usb_serial_driver safe_device = { 30618fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman .driver = { 30718fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman .owner = THIS_MODULE, 308269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman .name = "safe_serial", 30918fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman }, 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .id_table = id_table, 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .num_ports = 1, 31212e2e52cc578714d5824a27dd1a131a5418d636bJohan Hovold .process_read_urb = safe_process_read_urb, 313241c80ea72be4828c63f5dd44b142e54d0a12f5dJohan Hovold .prepare_write_buffer = safe_prepare_write_buffer, 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .attach = safe_startup, 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 317d860322f34e4a53f347b1aeae23d5b72f1e91b8cAlan Sternstatic struct usb_serial_driver * const serial_drivers[] = { 318d860322f34e4a53f347b1aeae23d5b72f1e91b8cAlan Stern &safe_device, NULL 319d860322f34e4a53f347b1aeae23d5b72f1e91b8cAlan Stern}; 320d860322f34e4a53f347b1aeae23d5b72f1e91b8cAlan Stern 32143c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Coxstatic int __init safe_init(void) 3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 323d860322f34e4a53f347b1aeae23d5b72f1e91b8cAlan Stern int i; 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 325c197a8db59daf06dc5e77acd5a9681329cb22458Greg Kroah-Hartman printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" 326c197a8db59daf06dc5e77acd5a9681329cb22458Greg Kroah-Hartman DRIVER_DESC "\n"); 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 32843c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Cox /* if we have vendor / product parameters patch them into id list */ 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (vendor || product) { 330c197a8db59daf06dc5e77acd5a9681329cb22458Greg Kroah-Hartman printk(KERN_INFO KBUILD_MODNAME ": vendor: %x product: %x\n", 331c197a8db59daf06dc5e77acd5a9681329cb22458Greg Kroah-Hartman vendor, product); 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 33352950ed40dc97456209979af1d8f51b63cf6dcabTobias Klauser for (i = 0; i < ARRAY_SIZE(id_table); i++) { 3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!id_table[i].idVendor && !id_table[i].idProduct) { 3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds id_table[i].idVendor = vendor; 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds id_table[i].idProduct = product; 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 342d860322f34e4a53f347b1aeae23d5b72f1e91b8cAlan Stern return usb_serial_register_drivers(&safe_driver, serial_drivers); 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 34543c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Coxstatic void __exit safe_exit(void) 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 347d860322f34e4a53f347b1aeae23d5b72f1e91b8cAlan Stern usb_serial_deregister_drivers(&safe_driver, serial_drivers); 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 35043c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Coxmodule_init(safe_init); 35143c8f435b2ca39f12ad89d91d9ee2be04a196f93Alan Coxmodule_exit(safe_exit); 352