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