safe_serial.c revision 4c4c9432a6c916729c7296c47fe93b053a73e20c
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Safe Encapsulated USB Serial Driver
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      Copyright (C) 2001 Lineo
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      Copyright (C) 2001 Hewlett-Packard
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	This program is free software; you can redistribute it and/or modify
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	it under the terms of the GNU General Public License as published by
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	the Free Software Foundation; either version 2 of the License, or
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	(at your option) any later version.
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * By:
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      Stuart Lynne <sl@lineo.com>, Tom Rushworth <tbr@lineo.com>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The encapsultaion is designed to overcome difficulties with some USB hardware.
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * While the USB protocol has a CRC over the data while in transit, i.e. while
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * being carried over the bus, there is no end to end protection. If the hardware
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * has any problems getting the data into or out of the USB transmit and receive
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * FIFO's then data can be lost.
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This protocol adds a two byte trailer to each USB packet to specify the number
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * of bytes of valid data and a 10 bit CRC that will allow the receiver to verify
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * that the entire USB packet was received without error.
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Because in this case the sender and receiver are the class and function drivers
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * there is now end to end protection.
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * There is an additional option that can be used to force all transmitted packets
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to be padded to the maximum packet size. This provides a work around for some
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * devices which have problems with small USB packets.
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Assuming a packetsize of N:
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      0..N-2  data and optional padding
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      N-2     bits 7-2 - number of bytes of valid data
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *              bits 1-0 top two bits of 10 bit CRC
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      N-1     bottom 8 bits of 10 bit CRC
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      | Data Length       | 10 bit CRC                                |
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      + 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 | 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 +
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The 10 bit CRC is computed across the sent data, followed by the trailer with
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the length set and the CRC set to zero. The CRC is then OR'd into the trailer.
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * When received a 10 bit CRC is computed over the entire frame including the trailer
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and should be equal to zero.
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Two module parameters are used to control the encapsulation, if both are
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * turned of the module works as a simple serial device with NO
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * encapsulation.
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * See linux/drivers/usbd/serial_fd for a device function driver
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * implementation of this.
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/config.h>
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "usb-serial.h"
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CONFIG_USB_SAFE_PADDED
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CONFIG_USB_SAFE_PADDED 0
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int debug;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int safe = 1;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int padded = CONFIG_USB_SAFE_PADDED;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_VERSION "v0.0b"
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_AUTHOR "sl@lineo.com, tbr@lineo.com"
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB Safe Encapsulated Serial"
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR (DRIVER_AUTHOR);
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION (DRIVER_DESC);
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined(CONFIG_USBD_SAFE_SERIAL_VENDOR) && !defined(CONFIG_USBD_SAFE_SERIAL_PRODUCT)
95521daed8db4443a1574a98fc3abae8e230f09eaaRandy Dunlap#error "SAFE_SERIAL_VENDOR defined without SAFE_SERIAL_PRODUCT"
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if ! defined(CONFIG_USBD_SAFE_SERIAL_VENDOR)
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic __u16 vendor;		// no default
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic __u16 product;		// no default
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(vendor, ushort, 0);
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(vendor, "User specified USB idVendor (required)");
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(product, ushort, 0);
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(product, "User specified USB idProduct (required)");
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(debug, bool, S_IRUGO | S_IWUSR);
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(debug, "Debug enabled or not");
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(safe, bool, 0);
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(safe, "Turn Safe Encapsulation On/Off");
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(padded, bool, 0);
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(padded, "Pad to full wMaxPacketSize On/Off");
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CDC_DEVICE_CLASS                        0x02
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CDC_INTERFACE_CLASS                     0x02
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CDC_INTERFACE_SUBCLASS                  0x06
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_INTERFACE_CLASS                   0xff
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_INTERFACE_SUBCLASS_SAFENET        0x01
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_SAFENET_CRC                       0x01
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_SAFENET_CRC_PADDED                0x02
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_INTERFACE_SUBCLASS_SAFESERIAL     0x02
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_SAFESERIAL_CRC                    0x01
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LINEO_SAFESERIAL_CRC_PADDED             0x02
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MY_USB_DEVICE(vend,prod,dc,ic,isc) \
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_CLASS | \
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS, \
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        .idVendor = (vend), \
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        .idProduct = (prod),\
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        .bDeviceClass = (dc),\
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        .bInterfaceClass = (ic), \
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        .bInterfaceSubClass = (isc),
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id id_table[] = {
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{MY_USB_DEVICE (0x49f, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	// Itsy
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{MY_USB_DEVICE (0x3f0, 0x2101, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	// Calypso
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{MY_USB_DEVICE (0x4dd, 0x8001, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	// Iris
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{MY_USB_DEVICE (0x4dd, 0x8002, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	// Collie
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{MY_USB_DEVICE (0x4dd, 0x8003, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	// Collie
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{MY_USB_DEVICE (0x4dd, 0x8004, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	// Collie
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{MY_USB_DEVICE (0x5f9, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},	// Sharp tmp
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined(CONFIG_USB_SAFE_SERIAL_VENDOR)
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{MY_USB_DEVICE
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 (CONFIG_USB_SAFE_SERIAL_VENDOR, CONFIG_USB_SAFE_SERIAL_PRODUCT, CDC_DEVICE_CLASS,
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// extra null entry for module
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// vendor/produc parameters
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{MY_USB_DEVICE (0, 0, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)},
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{}			// terminating entry
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, id_table);
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver safe_driver = {
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"safe_serial",
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	usb_serial_probe,
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	usb_serial_disconnect,
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	id_table,
167ba9dc657af86d05d2971633e57d1f6f94ed60472Greg Kroah-Hartman	.no_dynamic_id = 	1,
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1704c4c9432a6c916729c7296c47fe93b053a73e20cArjan van de Venstatic const __u16 crc10_table[256] = {
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x000, 0x233, 0x255, 0x066, 0x299, 0x0aa, 0x0cc, 0x2ff, 0x301, 0x132, 0x154, 0x367, 0x198, 0x3ab, 0x3cd, 0x1fe,
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x031, 0x202, 0x264, 0x057, 0x2a8, 0x09b, 0x0fd, 0x2ce, 0x330, 0x103, 0x165, 0x356, 0x1a9, 0x39a, 0x3fc, 0x1cf,
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x062, 0x251, 0x237, 0x004, 0x2fb, 0x0c8, 0x0ae, 0x29d, 0x363, 0x150, 0x136, 0x305, 0x1fa, 0x3c9, 0x3af, 0x19c,
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x053, 0x260, 0x206, 0x035, 0x2ca, 0x0f9, 0x09f, 0x2ac, 0x352, 0x161, 0x107, 0x334, 0x1cb, 0x3f8, 0x39e, 0x1ad,
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x0c4, 0x2f7, 0x291, 0x0a2, 0x25d, 0x06e, 0x008, 0x23b, 0x3c5, 0x1f6, 0x190, 0x3a3, 0x15c, 0x36f, 0x309, 0x13a,
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x0f5, 0x2c6, 0x2a0, 0x093, 0x26c, 0x05f, 0x039, 0x20a, 0x3f4, 0x1c7, 0x1a1, 0x392, 0x16d, 0x35e, 0x338, 0x10b,
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x0a6, 0x295, 0x2f3, 0x0c0, 0x23f, 0x00c, 0x06a, 0x259, 0x3a7, 0x194, 0x1f2, 0x3c1, 0x13e, 0x30d, 0x36b, 0x158,
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x097, 0x2a4, 0x2c2, 0x0f1, 0x20e, 0x03d, 0x05b, 0x268, 0x396, 0x1a5, 0x1c3, 0x3f0, 0x10f, 0x33c, 0x35a, 0x169,
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x188, 0x3bb, 0x3dd, 0x1ee, 0x311, 0x122, 0x144, 0x377, 0x289, 0x0ba, 0x0dc, 0x2ef, 0x010, 0x223, 0x245, 0x076,
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x1b9, 0x38a, 0x3ec, 0x1df, 0x320, 0x113, 0x175, 0x346, 0x2b8, 0x08b, 0x0ed, 0x2de, 0x021, 0x212, 0x274, 0x047,
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x1ea, 0x3d9, 0x3bf, 0x18c, 0x373, 0x140, 0x126, 0x315, 0x2eb, 0x0d8, 0x0be, 0x28d, 0x072, 0x241, 0x227, 0x014,
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x1db, 0x3e8, 0x38e, 0x1bd, 0x342, 0x171, 0x117, 0x324, 0x2da, 0x0e9, 0x08f, 0x2bc, 0x043, 0x270, 0x216, 0x025,
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x14c, 0x37f, 0x319, 0x12a, 0x3d5, 0x1e6, 0x180, 0x3b3, 0x24d, 0x07e, 0x018, 0x22b, 0x0d4, 0x2e7, 0x281, 0x0b2,
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x17d, 0x34e, 0x328, 0x11b, 0x3e4, 0x1d7, 0x1b1, 0x382, 0x27c, 0x04f, 0x029, 0x21a, 0x0e5, 0x2d6, 0x2b0, 0x083,
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x12e, 0x31d, 0x37b, 0x148, 0x3b7, 0x184, 0x1e2, 0x3d1, 0x22f, 0x01c, 0x07a, 0x249, 0x0b6, 0x285, 0x2e3, 0x0d0,
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x11f, 0x32c, 0x34a, 0x179, 0x386, 0x1b5, 0x1d3, 0x3e0, 0x21e, 0x02d, 0x04b, 0x278, 0x087, 0x2b4, 0x2d2, 0x0e1,
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CRC10_INITFCS     0x000	// Initial FCS value
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CRC10_GOODFCS     0x000	// Good final FCS value
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CRC10_FCS(fcs, c) ( (((fcs) << 8) & 0x3ff) ^ crc10_table[((fcs) >> 2) & 0xff] ^ (c))
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * fcs_compute10 - memcpy and calculate 10 bit CRC across buffer
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @sp: pointer to buffer
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @len: number of bytes
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @fcs: starting FCS
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Perform a memcpy and calculate fcs using ppp 10bit CRC algorithm. Return
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * new 10 bit FCS.
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic __u16 __inline__ fcs_compute10 (unsigned char *sp, int len, __u16 fcs)
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (; len-- > 0; fcs = CRC10_FCS (fcs, *sp++));
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return fcs;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void safe_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data = urb->transfer_buffer;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char length = urb->actual_length;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result;
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg ("%s", __FUNCTION__);
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (urb->status) {
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg ("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg ("safe_read_bulk_callback length: %d", port->read_urb->actual_length);
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ECHO_RCV
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int i;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned char *cp = port->read_urb->transfer_buffer;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < port->read_urb->actual_length; i++) {
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((i % 32) == 0) {
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk ("\nru[%02x] ", i);
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk ("%02x ", *cp++);
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk ("\n");
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (safe) {
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		__u16 fcs;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(fcs = fcs_compute10 (data, length, CRC10_INITFCS))) {
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int actual_length = data[length - 2] >> 2;
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (actual_length <= (length - 2)) {
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				info ("%s - actual: %d", __FUNCTION__, actual_length);
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				for (i = 0; i < actual_length; i++) {
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					tty_insert_flip_char (port->tty, data[i], 0);
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				tty_flip_buffer_push (port->tty);
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else {
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				err ("%s - inconsistent lengths %d:%d", __FUNCTION__,
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     actual_length, length);
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			err ("%s - bad CRC %x", __FUNCTION__, fcs);
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < length; i++) {
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			tty_insert_flip_char (port->tty, data[i], 0);
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty_flip_buffer_push (port->tty);
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Continue trying to always read  */
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_bulk_urb (urb, port->serial->dev,
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       usb_rcvbulkpipe (port->serial->dev, port->bulk_in_endpointAddress),
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       urb->transfer_buffer, urb->transfer_buffer_length,
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       safe_read_bulk_callback, port);
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((result = usb_submit_urb (urb, GFP_ATOMIC))) {
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err ("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int safe_write (struct usb_serial_port *port, const unsigned char *buf, int count)
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data;
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result;
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int packet_length;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg ("safe_write port: %p %d urb: %p count: %d", port, port->number, port->write_urb,
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	     count);
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!port->write_urb) {
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg ("%s - write urb NULL", __FUNCTION__);
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (0);
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg ("safe_write write_urb: %d transfer_buffer_length",
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	     port->write_urb->transfer_buffer_length);
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!port->write_urb->transfer_buffer_length) {
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg ("%s - write urb transfer_buffer_length zero", __FUNCTION__);
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (0);
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count == 0) {
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg ("%s - write request of 0 bytes", __FUNCTION__);
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (0);
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
302507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	spin_lock(&port->lock);
303507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	if (port->write_urb_busy) {
304507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman		spin_unlock(&port->lock);
305507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman		dbg("%s - already writing", __FUNCTION__);
306507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman		return 0;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
308507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	port->write_urb_busy = 1;
309507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	spin_unlock(&port->lock);
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	packet_length = port->bulk_out_size;	// get max packetsize
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = packet_length - (safe ? 2 : 0);	// get bytes to send
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	count = (count > i) ? i : count;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// get the data into the transfer buffer
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = port->write_urb->transfer_buffer;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset (data, '0', packet_length);
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy (data, buf, count);
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (safe) {
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		__u16 fcs;
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		// pad if necessary
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!padded) {
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			packet_length = count + 2;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		// set count
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data[packet_length - 2] = count << 2;
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data[packet_length - 1] = 0;
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		// compute fcs and insert into trailer
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fcs = fcs_compute10 (data, packet_length, CRC10_INITFCS);
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data[packet_length - 2] |= fcs >> 8;
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		data[packet_length - 1] |= fcs & 0xff;
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		// set length to send
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->write_urb->transfer_buffer_length = packet_length;
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->write_urb->transfer_buffer_length = count;
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer);
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ECHO_TX
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int i;
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned char *cp = port->write_urb->transfer_buffer;
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < port->write_urb->transfer_buffer_length; i++) {
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((i % 32) == 0) {
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk ("\nsu[%02x] ", i);
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk ("%02x ", *cp++);
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk ("\n");
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	port->write_urb->dev = port->serial->dev;
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((result = usb_submit_urb (port->write_urb, GFP_KERNEL))) {
361507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman		port->write_urb_busy = 0;
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err ("%s - failed submitting write urb, error %d", __FUNCTION__, result);
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg ("%s urb: %p submitted", __FUNCTION__, port->write_urb);
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (count);
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int safe_write_room (struct usb_serial_port *port)
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int room = 0;		// Default: no room
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg ("%s", __FUNCTION__);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
376507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	if (port->write_urb_busy)
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		room = port->bulk_out_size - (safe ? 2 : 0);
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (room) {
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg ("safe_write_room returns %d", room);
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (room);
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int safe_startup (struct usb_serial *serial)
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (serial->interface->cur_altsetting->desc.bInterfaceProtocol) {
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case LINEO_SAFESERIAL_CRC:
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case LINEO_SAFESERIAL_CRC_PADDED:
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		padded = 1;
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
400ea65370d025f5005649e5cb37c4d025e92c6fc38Greg Kroah-Hartmanstatic struct usb_serial_driver safe_device = {
40118fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	.driver = {
40218fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman		.owner =	THIS_MODULE,
403269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman		.name =		"safe_serial",
40418fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	},
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =		id_table,
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_interrupt_in =	NUM_DONT_CARE,
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_bulk_in =		NUM_DONT_CARE,
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_bulk_out =		NUM_DONT_CARE,
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_ports =		1,
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		safe_write,
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		safe_write_room,
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_bulk_callback =	safe_read_bulk_callback,
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.attach =		safe_startup,
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init safe_init (void)
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, retval;
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info (DRIVER_VERSION " " DRIVER_AUTHOR);
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info (DRIVER_DESC);
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info ("vendor: %x product: %x safe: %d padded: %d\n", vendor, product, safe, padded);
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// if we have vendor / product parameters patch them into id list
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (vendor || product) {
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info ("vendor: %x product: %x\n", vendor, product);
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < (sizeof (id_table) / sizeof (struct usb_device_id)); i++) {
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!id_table[i].idVendor && !id_table[i].idProduct) {
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				id_table[i].idVendor = vendor;
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				id_table[i].idProduct = product;
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_serial_register(&safe_device);
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_usb_serial_register;
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&safe_driver);
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_usb_register;
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_usb_register:
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister(&safe_device);
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_usb_serial_register:
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit safe_exit (void)
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister (&safe_driver);
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister (&safe_device);
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init (safe_init);
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit (safe_exit);
459