Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

715
drivers/usb/serial/Kconfig Normal file
View file

@ -0,0 +1,715 @@
#
# USB Serial device configuration
#
menuconfig USB_SERIAL
tristate "USB Serial Converter support"
depends on TTY
---help---
Say Y here if you have a USB device that provides normal serial
ports, or acts like a serial device, and you want to connect it to
your USB bus.
Please read <file:Documentation/usb/usb-serial.txt> for more
information on the specifics of the different devices that are
supported, and on how to use them.
To compile this driver as a module, choose M here: the
module will be called usbserial.
if USB_SERIAL
config USB_SERIAL_CONSOLE
bool "USB Serial Console device support"
depends on USB_SERIAL=y
---help---
If you say Y here, it will be possible to use a USB to serial
converter port as the system console (the system console is the
device which receives all kernel messages and warnings and which
allows logins in single user mode). This could be useful if some
terminal or printer is connected to that serial port.
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
"console=ttyUSB0". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
If you don't have a VGA card installed and you say Y here, the
kernel will automatically use the first USB to serial converter
port, /dev/ttyUSB0, as system console.
If unsure, say N.
config USB_SERIAL_GENERIC
bool "USB Generic Serial Driver"
help
Say Y here if you want to use the generic USB serial driver. Please
read <file:Documentation/usb/usb-serial.txt> for more information on
using this driver. It is recommended that the "USB Serial converter
support" be compiled as a module for this driver to be used
properly.
config USB_SERIAL_SIMPLE
tristate "USB Serial Simple Driver"
help
Say Y here to use the USB serial "simple" driver. This driver
handles a wide range of very simple devices, all in one
driver. Specifically, it supports:
- Suunto ANT+ USB device.
- Medtronic CareLink USB device
- Fundamental Software dongle.
- HP4x calculators
- a number of Motorola phones
- Novatel Wireless GPS receivers
- Siemens USB/MPI adapter.
- ViVOtech ViVOpay USB device.
- Infineon Modem Flashloader USB interface
- ZIO Motherboard USB serial interface
To compile this driver as a module, choose M here: the module
will be called usb-serial-simple.
config USB_SERIAL_AIRCABLE
tristate "USB AIRcable Bluetooth Dongle Driver"
help
Say Y here if you want to use USB AIRcable Bluetooth Dongle.
To compile this driver as a module, choose M here: the module
will be called aircable.
config USB_SERIAL_ARK3116
tristate "USB ARK Micro 3116 USB Serial Driver"
help
Say Y here if you want to use a ARK Micro 3116 USB to Serial
device.
To compile this driver as a module, choose M here: the
module will be called ark3116
config USB_SERIAL_BELKIN
tristate "USB Belkin and Peracom Single Port Serial Driver"
help
Say Y here if you want to use a Belkin USB Serial single port
adaptor (F5U103 is one of the model numbers) or the Peracom single
port USB to serial adapter.
To compile this driver as a module, choose M here: the
module will be called belkin_sa.
config USB_SERIAL_CH341
tristate "USB Winchiphead CH341 Single Port Serial Driver"
help
Say Y here if you want to use a Winchiphead CH341 single port
USB to serial adapter.
To compile this driver as a module, choose M here: the
module will be called ch341.
config USB_SERIAL_WHITEHEAT
tristate "USB ConnectTech WhiteHEAT Serial Driver"
select USB_EZUSB_FX2
help
Say Y here if you want to use a ConnectTech WhiteHEAT 4 port
USB to serial converter device.
To compile this driver as a module, choose M here: the
module will be called whiteheat.
config USB_SERIAL_DIGI_ACCELEPORT
tristate "USB Digi International AccelePort USB Serial Driver"
---help---
Say Y here if you want to use Digi AccelePort USB 2 or 4 devices,
2 port (plus parallel port) and 4 port USB serial converters. The
parallel port on the USB 2 appears as a third serial port on Linux.
The Digi Acceleport USB 8 is not yet supported by this driver.
This driver works under SMP with the usb-uhci driver. It does not
work under SMP with the uhci driver.
To compile this driver as a module, choose M here: the
module will be called digi_acceleport.
config USB_SERIAL_CP210X
tristate "USB CP210x family of UART Bridge Controllers"
help
Say Y here if you want to use a CP2101/CP2102/CP2103 based USB
to RS232 converters.
To compile this driver as a module, choose M here: the
module will be called cp210x.
config USB_SERIAL_CYPRESS_M8
tristate "USB Cypress M8 USB Serial Driver"
help
Say Y here if you want to use a device that contains the Cypress
USB to Serial microcontroller, such as the DeLorme Earthmate GPS.
Attempted SMP support... send bug reports!
Supported microcontrollers in the CY4601 family are:
CY7C63741 CY7C63742 CY7C63743 CY7C64013
To compile this driver as a module, choose M here: the
module will be called cypress_m8.
config USB_SERIAL_EMPEG
tristate "USB Empeg empeg-car Mark I/II Driver"
help
Say Y here if you want to connect to your Empeg empeg-car Mark I/II
mp3 player via USB. The driver uses a single ttyUSB{0,1,2,...}
device node. See <file:Documentation/usb/usb-serial.txt> for more
tidbits of information.
To compile this driver as a module, choose M here: the
module will be called empeg.
config USB_SERIAL_FTDI_SIO
tristate "USB FTDI Single Port Serial Driver"
---help---
Say Y here if you want to use a FTDI SIO single port USB to serial
converter device. The implementation I have is called the USC-1000.
This driver has also be tested with the 245 and 232 devices.
See <http://ftdi-usb-sio.sourceforge.net/> for more
information on this driver and the device.
To compile this driver as a module, choose M here: the
module will be called ftdi_sio.
config USB_SERIAL_VISOR
tristate "USB Handspring Visor / Palm m50x / Sony Clie Driver"
help
Say Y here if you want to connect to your HandSpring Visor, Palm
m500 or m505 through its USB docking station. See
<http://usbvisor.sourceforge.net/index.php3> for more information on using this
driver.
To compile this driver as a module, choose M here: the
module will be called visor.
config USB_SERIAL_IPAQ
tristate "USB PocketPC PDA Driver"
help
Say Y here if you want to connect to your Compaq iPAQ, HP Jornada
or any other PDA running Windows CE 3.0 or PocketPC 2002
using a USB cradle/cable. For information on using the driver,
read <file:Documentation/usb/usb-serial.txt>.
To compile this driver as a module, choose M here: the
module will be called ipaq.
config USB_SERIAL_IR
tristate "USB IR Dongle Serial Driver"
help
Say Y here if you want to enable simple serial support for USB IrDA
devices. This is useful if you do not want to use the full IrDA
stack.
To compile this driver as a module, choose M here: the
module will be called ir-usb.
config USB_SERIAL_EDGEPORT
tristate "USB Inside Out Edgeport Serial Driver"
---help---
Say Y here if you want to use any of the following devices from
Inside Out Networks (Digi):
Edgeport/4
Rapidport/4
Edgeport/4t
Edgeport/2
Edgeport/4i
Edgeport/2i
Edgeport/421
Edgeport/21
Edgeport/8
Edgeport/8 Dual
Edgeport/2D8
Edgeport/4D8
Edgeport/8i
Edgeport/2 DIN
Edgeport/4 DIN
Edgeport/16 Dual
To compile this driver as a module, choose M here: the
module will be called io_edgeport.
config USB_SERIAL_EDGEPORT_TI
tristate "USB Inside Out Edgeport Serial Driver (TI devices)"
help
Say Y here if you want to use any of the devices from Inside Out
Networks (Digi) that are not supported by the io_edgeport driver.
This includes the Edgeport/1 device.
To compile this driver as a module, choose M here: the
module will be called io_ti.
config USB_SERIAL_F81232
tristate "USB Fintek F81232 Single Port Serial Driver"
help
Say Y here if you want to use the Fintek F81232 single
port usb to serial adapter.
To compile this driver as a module, choose M here: the
module will be called f81232.
config USB_SERIAL_GARMIN
tristate "USB Garmin GPS driver"
help
Say Y here if you want to connect to your Garmin GPS.
Should work with most Garmin GPS devices which have a native USB port.
See <http://sourceforge.net/projects/garmin-gps> for the latest
version of the driver.
To compile this driver as a module, choose M here: the
module will be called garmin_gps.
config USB_SERIAL_IPW
tristate "USB IPWireless (3G UMTS TDD) Driver"
select USB_SERIAL_WWAN
help
Say Y here if you want to use a IPWireless USB modem such as
the ones supplied by Axity3G/Sentech South Africa.
To compile this driver as a module, choose M here: the
module will be called ipw.
config USB_SERIAL_IUU
tristate "USB Infinity USB Unlimited Phoenix Driver"
help
Say Y here if you want to use a IUU in phoenix mode and get
an extra ttyUSBx device. More information available on
http://eczema.ecze.com/iuu_phoenix.html
To compile this driver as a module, choose M here: the
module will be called iuu_phoenix.o
config USB_SERIAL_KEYSPAN_PDA
tristate "USB Keyspan PDA Single Port Serial Driver"
select USB_EZUSB_FX2
help
Say Y here if you want to use a Keyspan PDA single port USB to
serial converter device. This driver makes use of firmware
developed from scratch by Brian Warner.
To compile this driver as a module, choose M here: the
module will be called keyspan_pda.
config USB_SERIAL_KEYSPAN
tristate "USB Keyspan USA-xxx Serial Driver"
select USB_EZUSB_FX2
---help---
Say Y here if you want to use Keyspan USB to serial converter
devices. This driver makes use of Keyspan's official firmware
and was developed with their support. You must also include
firmware to support your particular device(s).
See <http://blemings.org/hugh/keyspan.html> for more information.
To compile this driver as a module, choose M here: the
module will be called keyspan.
config USB_SERIAL_KEYSPAN_MPR
bool "USB Keyspan MPR Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the Keyspan MPR converter.
config USB_SERIAL_KEYSPAN_USA28
bool "USB Keyspan USA-28 Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the USA-28 converter.
config USB_SERIAL_KEYSPAN_USA28X
bool "USB Keyspan USA-28X Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the USA-28X converter.
Be sure you have a USA-28X, there are also 28XA and 28XB
models, the label underneath has the actual part number.
config USB_SERIAL_KEYSPAN_USA28XA
bool "USB Keyspan USA-28XA Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the USA-28XA converter.
Be sure you have a USA-28XA, there are also 28X and 28XB
models, the label underneath has the actual part number.
config USB_SERIAL_KEYSPAN_USA28XB
bool "USB Keyspan USA-28XB Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the USA-28XB converter.
Be sure you have a USA-28XB, there are also 28X and 28XA
models, the label underneath has the actual part number.
config USB_SERIAL_KEYSPAN_USA19
bool "USB Keyspan USA-19 Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the USA-19 converter.
config USB_SERIAL_KEYSPAN_USA18X
bool "USB Keyspan USA-18X Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the USA-18X converter.
config USB_SERIAL_KEYSPAN_USA19W
bool "USB Keyspan USA-19W Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the USA-19W converter.
config USB_SERIAL_KEYSPAN_USA19QW
bool "USB Keyspan USA-19QW Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the USA-19QW converter.
config USB_SERIAL_KEYSPAN_USA19QI
bool "USB Keyspan USA-19QI Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the USA-19QI converter.
config USB_SERIAL_KEYSPAN_USA49W
bool "USB Keyspan USA-49W Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the USA-49W converter.
config USB_SERIAL_KEYSPAN_USA49WLC
bool "USB Keyspan USA-49WLC Firmware"
depends on USB_SERIAL_KEYSPAN && FIRMWARE_IN_KERNEL
help
Say Y here to include firmware for the USA-49WLC converter.
config USB_SERIAL_KLSI
tristate "USB KL5KUSB105 (Palmconnect) Driver"
---help---
Say Y here if you want to use a KL5KUSB105 - based single port
serial adapter. The most widely known -- and currently the only
tested -- device in this category is the PalmConnect USB Serial
adapter sold by Palm Inc. for use with their Palm III and Palm V
series PDAs.
Please read <file:Documentation/usb/usb-serial.txt> for more
information.
To compile this driver as a module, choose M here: the
module will be called kl5kusb105.
config USB_SERIAL_KOBIL_SCT
tristate "USB KOBIL chipcard reader"
---help---
Say Y here if you want to use one of the following KOBIL USB chipcard
readers:
- USB TWIN
- KAAN Standard Plus
- KAAN SIM
- SecOVID Reader Plus
- B1 Professional
- KAAN Professional
Note that you need a current CT-API.
To compile this driver as a module, choose M here: the
module will be called kobil_sct.
config USB_SERIAL_MCT_U232
tristate "USB MCT Single Port Serial Driver"
---help---
Say Y here if you want to use a USB Serial single port adapter from
Magic Control Technology Corp. (U232 is one of the model numbers).
This driver also works with Sitecom U232-P25 and D-Link DU-H3SP USB
BAY, Belkin F5U109, and Belkin F5U409 devices.
To compile this driver as a module, choose M here: the
module will be called mct_u232.
config USB_SERIAL_METRO
tristate "USB Metrologic Instruments USB-POS Barcode Scanner Driver"
---help---
Say Y here if you want to use a USB POS Metrologic barcode scanner.
To compile this driver as a module, choose M here: the
module will be called metro-usb.
config USB_SERIAL_MOS7720
tristate "USB Moschip 7720 Serial Driver"
---help---
Say Y here if you want to use USB Serial single and double
port adapters from Moschip Semiconductor Tech.
To compile this driver as a module, choose M here: the
module will be called mos7720.
config USB_SERIAL_MOS7715_PARPORT
bool "Support for parallel port on the Moschip 7715"
depends on USB_SERIAL_MOS7720
depends on PARPORT=y || PARPORT=USB_SERIAL_MOS7720
select PARPORT_NOT_PC
---help---
Say Y if you have a Moschip 7715 device and would like to use
the parallel port it provides. The port will register with
the parport subsystem as a low-level driver.
config USB_SERIAL_MOS7840
tristate "USB Moschip 7840/7820 USB Serial Driver"
---help---
Say Y here if you want to use a MCS7840 Quad-Serial or MCS7820
Dual-Serial port device from MosChip Semiconductor.
The MCS7840 and MCS7820 have been developed to connect a wide range
of standard serial devices to a USB host. The MCS7840 has a USB
device controller connected to four (4) individual UARTs while the
MCS7820 controller connects to two (2) individual UARTs.
To compile this driver as a module, choose M here: the
module will be called mos7840. If unsure, choose N.
config USB_SERIAL_MXUPORT
tristate "USB Moxa UPORT Serial Driver"
---help---
Say Y here if you want to use a MOXA UPort Serial hub.
This driver supports:
[2 Port]
- UPort 1250 : 2 Port RS-232/422/485 USB to Serial Hub
- UPort 1250I : 2 Port RS-232/422/485 USB to Serial Hub with
Isolation
[4 Port]
- UPort 1410 : 4 Port RS-232 USB to Serial Hub
- UPort 1450 : 4 Port RS-232/422/485 USB to Serial Hub
- UPort 1450I : 4 Port RS-232/422/485 USB to Serial Hub with
Isolation
[8 Port]
- UPort 1610-8 : 8 Port RS-232 USB to Serial Hub
- UPort 1650-8 : 8 Port RS-232/422/485 USB to Serial Hub
[16 Port]
- UPort 1610-16 : 16 Port RS-232 USB to Serial Hub
- UPort 1650-16 : 16 Port RS-232/422/485 USB to Serial Hub
To compile this driver as a module, choose M here: the
module will be called mxuport.
config USB_SERIAL_NAVMAN
tristate "USB Navman GPS device"
help
To compile this driver as a module, choose M here: the
module will be called navman.
config USB_SERIAL_PL2303
tristate "USB Prolific 2303 Single Port Serial Driver"
help
Say Y here if you want to use the PL2303 USB Serial single port
adapter from Prolific.
To compile this driver as a module, choose M here: the
module will be called pl2303.
config USB_SERIAL_OTI6858
tristate "USB Ours Technology Inc. OTi-6858 USB To RS232 Bridge Controller"
help
Say Y here if you want to use the OTi-6858 single port USB to serial
converter device.
To compile this driver as a module, choose M here: the
module will be called oti6858.
config USB_SERIAL_QCAUX
tristate "USB Qualcomm Auxiliary Serial Port Driver"
help
Say Y here if you want to use the auxiliary serial ports provided
by many modems based on Qualcomm chipsets. These ports often use
a proprietary protocol called DM and cannot be used for AT- or
PPP-based communication.
To compile this driver as a module, choose M here: the
module will be called qcaux. If unsure, choose N.
config USB_SERIAL_QUALCOMM
tristate "USB Qualcomm Serial modem"
select USB_SERIAL_WWAN
help
Say Y here if you have a Qualcomm USB modem device. These are
usually wireless cellular modems.
To compile this driver as a module, choose M here: the
module will be called qcserial.
config USB_SERIAL_SPCP8X5
tristate "USB SPCP8x5 USB To Serial Driver"
help
Say Y here if you want to use the spcp8x5 converter chip. This is
commonly found in some Z-Wave USB devices.
To compile this driver as a module, choose M here: the
module will be called spcp8x5.
config USB_SERIAL_SAFE
tristate "USB Safe Serial (Encapsulated) Driver"
config USB_SERIAL_SAFE_PADDED
bool "USB Secure Encapsulated Driver - Padded"
depends on USB_SERIAL_SAFE
config USB_SERIAL_SIERRAWIRELESS
tristate "USB Sierra Wireless Driver"
help
Say M here if you want to use Sierra Wireless devices.
Many devices have a feature known as TRU-Install. For those devices
to work properly, the USB Storage Sierra feature must be enabled.
To compile this driver as a module, choose M here: the
module will be called sierra.
config USB_SERIAL_SYMBOL
tristate "USB Symbol Barcode driver (serial mode)"
help
Say Y here if you want to use a Symbol USB Barcode device
in serial emulation mode.
To compile this driver as a module, choose M here: the
module will be called symbolserial.
config USB_SERIAL_TI
tristate "USB TI 3410/5052 Serial Driver"
help
Say Y here if you want to use the TI USB 3410 or 5052
serial devices.
To compile this driver as a module, choose M here: the
module will be called ti_usb_3410_5052.
config USB_SERIAL_CYBERJACK
tristate "USB REINER SCT cyberJack pinpad/e-com chipcard reader"
---help---
Say Y here if you want to use a cyberJack pinpad/e-com USB chipcard
reader. This is an interface to ISO 7816 compatible contact-based
chipcards, e.g. GSM SIMs.
To compile this driver as a module, choose M here: the
module will be called cyberjack.
If unsure, say N.
config USB_SERIAL_XIRCOM
tristate "USB Xircom / Entregra Single Port Serial Driver"
select USB_EZUSB_FX2
help
Say Y here if you want to use a Xircom or Entregra single port USB to
serial converter device. This driver makes use of firmware
developed from scratch by Brian Warner.
To compile this driver as a module, choose M here: the
module will be called keyspan_pda.
config USB_SERIAL_WWAN
tristate
config USB_SERIAL_OPTION
tristate "USB driver for GSM and CDMA modems"
select USB_SERIAL_WWAN
help
Say Y here if you have a GSM or CDMA modem that's connected to USB.
This driver also supports several PCMCIA cards which have a
built-in OHCI-USB adapter and an internally-connected GSM modem.
The USB bus on these cards is not accessible externally.
Supported devices include (some of?) those made by:
Option, Huawei, Audiovox, Novatel Wireless, or Anydata.
To compile this driver as a module, choose M here: the
module will be called option.
If this driver doesn't recognize your device,
it might be accessible via the FTDI_SIO driver.
config USB_SERIAL_OMNINET
tristate "USB ZyXEL omni.net LCD Plus Driver"
help
Say Y here if you want to use a ZyXEL omni.net LCD ISDN TA.
To compile this driver as a module, choose M here: the
module will be called omninet.
config USB_SERIAL_OPTICON
tristate "USB Opticon Barcode driver (serial mode)"
help
Say Y here if you want to use a Opticon USB Barcode device
in serial emulation mode.
To compile this driver as a module, choose M here: the
module will be called opticon.
config USB_SERIAL_XSENS_MT
tristate "Xsens motion tracker serial interface driver"
help
Say Y here if you want to use Xsens motion trackers.
This driver supports the new generation of motion trackers
by Xsens. Older devices can be accessed using the FTDI_SIO
driver.
To compile this driver as a module, choose M here: the
module will be called xsens_mt.
config USB_SERIAL_WISHBONE
tristate "USB-Wishbone adapter interface driver"
help
Say Y here if you want to use a USB attached Wishbone bus.
Wishbone is an open hardware SoC bus commonly used in FPGA
designs. Bus access can be serialized using the Etherbone
protocol <http://www.ohwr.org/projects/etherbone-core>.
This driver is intended to be used with devices which attach
their internal Wishbone bus to a USB serial interface using
the Etherbone protocol. A userspace library is required to
speak the protocol made available by this driver as ttyUSBx.
To compile this driver as a module, choose M here: the
module will be called wishbone-serial.
config USB_SERIAL_SSU100
tristate "USB Quatech SSU-100 Single Port Serial Driver"
help
Say Y here if you want to use the Quatech SSU-100 single
port usb to serial adapter.
To compile this driver as a module, choose M here: the
module will be called ssu100.
config USB_SERIAL_QT2
tristate "USB Quatech Serial Driver for USB 2 devices"
help
Say Y here if you want to use the Quatech USB 2
serial adapters.
To compile this driver as a module, choose M here: the
module will be called quatech-serial.
config USB_SERIAL_DEBUG
tristate "USB Debugging Device"
help
Say Y here if you have a USB debugging device used to receive
debugging data from another machine. The most common of these
devices is the NetChip TurboCONNECT device.
To compile this driver as a module, choose M here: the
module will be called usb-debug.
endif # USB_SERIAL

View file

@ -0,0 +1,62 @@
#
# Makefile for the USB serial device drivers.
#
# Object file lists.
obj-$(CONFIG_USB_SERIAL) += usbserial.o
usbserial-y := usb-serial.o generic.o bus.o
usbserial-$(CONFIG_USB_SERIAL_CONSOLE) += console.o
obj-$(CONFIG_USB_SERIAL_AIRCABLE) += aircable.o
obj-$(CONFIG_USB_SERIAL_ARK3116) += ark3116.o
obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o
obj-$(CONFIG_USB_SERIAL_CH341) += ch341.o
obj-$(CONFIG_USB_SERIAL_CP210X) += cp210x.o
obj-$(CONFIG_USB_SERIAL_CYBERJACK) += cyberjack.o
obj-$(CONFIG_USB_SERIAL_CYPRESS_M8) += cypress_m8.o
obj-$(CONFIG_USB_SERIAL_DEBUG) += usb_debug.o
obj-$(CONFIG_USB_SERIAL_DIGI_ACCELEPORT) += digi_acceleport.o
obj-$(CONFIG_USB_SERIAL_EDGEPORT) += io_edgeport.o
obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += io_ti.o
obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o
obj-$(CONFIG_USB_SERIAL_F81232) += f81232.o
obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o
obj-$(CONFIG_USB_SERIAL_GARMIN) += garmin_gps.o
obj-$(CONFIG_USB_SERIAL_IPAQ) += ipaq.o
obj-$(CONFIG_USB_SERIAL_IPW) += ipw.o
obj-$(CONFIG_USB_SERIAL_IR) += ir-usb.o
obj-$(CONFIG_USB_SERIAL_IUU) += iuu_phoenix.o
obj-$(CONFIG_USB_SERIAL_KEYSPAN) += keyspan.o
obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o
obj-$(CONFIG_USB_SERIAL_KLSI) += kl5kusb105.o
obj-$(CONFIG_USB_SERIAL_KOBIL_SCT) += kobil_sct.o
obj-$(CONFIG_USB_SERIAL_MCT_U232) += mct_u232.o
obj-$(CONFIG_USB_SERIAL_METRO) += metro-usb.o
obj-$(CONFIG_USB_SERIAL_MOS7720) += mos7720.o
obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o
obj-$(CONFIG_USB_SERIAL_MXUPORT) += mxuport.o
obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o
obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o
obj-$(CONFIG_USB_SERIAL_OPTICON) += opticon.o
obj-$(CONFIG_USB_SERIAL_OPTION) += option.o
obj-$(CONFIG_USB_SERIAL_OTI6858) += oti6858.o
obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o
obj-$(CONFIG_USB_SERIAL_QCAUX) += qcaux.o
obj-$(CONFIG_USB_SERIAL_QUALCOMM) += qcserial.o
obj-$(CONFIG_USB_SERIAL_QT2) += quatech2.o
obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o
obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o
obj-$(CONFIG_USB_SERIAL_SIMPLE) += usb-serial-simple.o
obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o
obj-$(CONFIG_USB_SERIAL_SSU100) += ssu100.o
obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o
obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o
obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o
obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o
obj-$(CONFIG_USB_SERIAL_WISHBONE) += wishbone-serial.o
obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o
obj-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda.o
obj-$(CONFIG_USB_SERIAL_XSENS_MT) += xsens_mt.o

View file

@ -0,0 +1,16 @@
# some rules to handle the quirks of the 'as31' assembler, like
# insisting upon fixed suffixes for the input and output files,
# and its lack of preprocessor support
all: keyspan_pda_fw.h
%.asm: %.S
gcc -x assembler-with-cpp -P -E -o $@ $<
%.hex: %.asm
as31 -l $<
mv $*.obj $@
%_fw.h: %.hex ezusb_convert.pl
perl ezusb_convert.pl $* < $< > $@

View file

@ -0,0 +1,184 @@
/*
* AIRcable USB Bluetooth Dongle Driver.
*
* Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
* Copyright (C) 2006 Manuel Francisco Naranjo (naranjo.manuel@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 as published by the
* Free Software Foundation.
*
* The device works as an standard CDC device, it has 2 interfaces, the first
* one is for firmware access and the second is the serial one.
* The protocol is very simply, there are two possibilities reading or writing.
* When writing the first urb must have a Header that starts with 0x20 0x29 the
* next two bytes must say how much data will be sent.
* When reading the process is almost equal except that the header starts with
* 0x00 0x20.
*
* The device simply need some stuff to understand data coming from the usb
* buffer: The First and Second byte is used for a Header, the Third and Fourth
* tells the device the amount of information the package holds.
* Packages are 60 bytes long Header Stuff.
* When writing to the device the first two bytes of the header are 0x20 0x29
* When reading the bytes are 0x00 0x20, or 0x00 0x10, there is an strange
* situation, when too much data arrives to the device because it sends the data
* but with out the header. I will use a simply hack to override this situation,
* if there is data coming that does not contain any header, then that is data
* that must go directly to the tty, as there is no documentation about if there
* is any other control code, I will simply check for the first
* one.
*
* The driver registers himself with the USB-serial core and the USB Core. I had
* to implement a probe function against USB-serial, because other way, the
* driver was attaching himself to both interfaces. I have tried with different
* configurations of usb_serial_driver with out exit, only the probe function
* could handle this correctly.
*
* I have taken some info from a Greg Kroah-Hartman article:
* http://www.linuxjournal.com/article/6573
* And from Linux Device Driver Kit CD, which is a great work, the authors taken
* the work to recompile lots of information an knowledge in drivers development
* and made it all available inside a cd.
* URL: http://kernel.org/pub/linux/kernel/people/gregkh/ddk/
*
*/
#include <asm/unaligned.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/tty_flip.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
/* Vendor and Product ID */
#define AIRCABLE_VID 0x16CA
#define AIRCABLE_USB_PID 0x1502
/* Protocol Stuff */
#define HCI_HEADER_LENGTH 0x4
#define TX_HEADER_0 0x20
#define TX_HEADER_1 0x29
#define RX_HEADER_0 0x00
#define RX_HEADER_1 0x20
#define HCI_COMPLETE_FRAME 64
/* rx_flags */
#define THROTTLED 0x01
#define ACTUALLY_THROTTLED 0x02
#define DRIVER_AUTHOR "Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>, Johan Hovold <jhovold@gmail.com>"
#define DRIVER_DESC "AIRcable USB Driver"
/* ID table that will be registered with USB core */
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(AIRCABLE_VID, AIRCABLE_USB_PID) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
static int aircable_prepare_write_buffer(struct usb_serial_port *port,
void *dest, size_t size)
{
int count;
unsigned char *buf = dest;
count = kfifo_out_locked(&port->write_fifo, buf + HCI_HEADER_LENGTH,
size - HCI_HEADER_LENGTH, &port->lock);
buf[0] = TX_HEADER_0;
buf[1] = TX_HEADER_1;
put_unaligned_le16(count, &buf[2]);
return count + HCI_HEADER_LENGTH;
}
static int aircable_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
struct usb_host_interface *iface_desc = serial->interface->
cur_altsetting;
struct usb_endpoint_descriptor *endpoint;
int num_bulk_out = 0;
int i;
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
endpoint = &iface_desc->endpoint[i].desc;
if (usb_endpoint_is_bulk_out(endpoint)) {
dev_dbg(&serial->dev->dev,
"found bulk out on endpoint %d\n", i);
++num_bulk_out;
}
}
if (num_bulk_out == 0) {
dev_dbg(&serial->dev->dev, "Invalid interface, discarding\n");
return -ENODEV;
}
return 0;
}
static int aircable_process_packet(struct usb_serial_port *port,
int has_headers, char *packet, int len)
{
if (has_headers) {
len -= HCI_HEADER_LENGTH;
packet += HCI_HEADER_LENGTH;
}
if (len <= 0) {
dev_dbg(&port->dev, "%s - malformed packet\n", __func__);
return 0;
}
tty_insert_flip_string(&port->port, packet, len);
return len;
}
static void aircable_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
char *data = (char *)urb->transfer_buffer;
int has_headers;
int count;
int len;
int i;
has_headers = (urb->actual_length > 2 && data[0] == RX_HEADER_0);
count = 0;
for (i = 0; i < urb->actual_length; i += HCI_COMPLETE_FRAME) {
len = min_t(int, urb->actual_length - i, HCI_COMPLETE_FRAME);
count += aircable_process_packet(port, has_headers,
&data[i], len);
}
if (count)
tty_flip_buffer_push(&port->port);
}
static struct usb_serial_driver aircable_device = {
.driver = {
.owner = THIS_MODULE,
.name = "aircable",
},
.id_table = id_table,
.num_ports = 1,
.bulk_out_size = HCI_COMPLETE_FRAME,
.probe = aircable_probe,
.process_read_urb = aircable_process_read_urb,
.prepare_write_buffer = aircable_prepare_write_buffer,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
};
static struct usb_serial_driver * const serial_drivers[] = {
&aircable_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,791 @@
/*
* Copyright (C) 2009 by Bart Hartgers (bart.hartgers+ark3116@gmail.com)
* Original version:
* Copyright (C) 2006
* Simon Schulz (ark3116_driver <at> auctionant.de)
*
* ark3116
* - implements a driver for the arkmicro ark3116 chipset (vendor=0x6547,
* productid=0x0232) (used in a datacable called KQ-U8A)
*
* Supports full modem status lines, break, hardware flow control. Does not
* support software flow control, since I do not know how to enable it in hw.
*
* This driver is a essentially new implementation. I initially dug
* into the old ark3116.c driver and suddenly realized the ark3116 is
* a 16450 with a USB interface glued to it. See comments at the
* bottom of this file.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/kernel.h>
#include <linux/ioctl.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#define DRIVER_AUTHOR "Bart Hartgers <bart.hartgers+ark3116@gmail.com>"
#define DRIVER_DESC "USB ARK3116 serial/IrDA driver"
#define DRIVER_DEV_DESC "ARK3116 RS232/IrDA"
#define DRIVER_NAME "ark3116"
/* usb timeout of 1 second */
#define ARK_TIMEOUT 1000
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x6547, 0x0232) },
{ USB_DEVICE(0x18ec, 0x3118) }, /* USB to IrDA adapter */
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
static int is_irda(struct usb_serial *serial)
{
struct usb_device *dev = serial->dev;
if (le16_to_cpu(dev->descriptor.idVendor) == 0x18ec &&
le16_to_cpu(dev->descriptor.idProduct) == 0x3118)
return 1;
return 0;
}
struct ark3116_private {
int irda; /* 1 for irda device */
/* protects hw register updates */
struct mutex hw_lock;
int quot; /* baudrate divisor */
__u32 lcr; /* line control register value */
__u32 hcr; /* handshake control register (0x8)
* value */
__u32 mcr; /* modem control register value */
/* protects the status values below */
spinlock_t status_lock;
__u32 msr; /* modem status register value */
__u32 lsr; /* line status register value */
};
static int ark3116_write_reg(struct usb_serial *serial,
unsigned reg, __u8 val)
{
int result;
/* 0xfe 0x40 are magic values taken from original driver */
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
0xfe, 0x40, val, reg,
NULL, 0, ARK_TIMEOUT);
return result;
}
static int ark3116_read_reg(struct usb_serial *serial,
unsigned reg, unsigned char *buf)
{
int result;
/* 0xfe 0xc0 are magic values taken from original driver */
result = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
0xfe, 0xc0, 0, reg,
buf, 1, ARK_TIMEOUT);
if (result < 0)
return result;
else
return buf[0];
}
static inline int calc_divisor(int bps)
{
/* Original ark3116 made some exceptions in rounding here
* because windows did the same. Assume that is not really
* necessary.
* Crystal is 12MHz, probably because of USB, but we divide by 4?
*/
return (12000000 + 2*bps) / (4*bps);
}
static int ark3116_attach(struct usb_serial *serial)
{
/* make sure we have our end-points */
if ((serial->num_bulk_in == 0) ||
(serial->num_bulk_out == 0) ||
(serial->num_interrupt_in == 0)) {
dev_err(&serial->dev->dev,
"%s - missing endpoint - "
"bulk in: %d, bulk out: %d, int in %d\n",
KBUILD_MODNAME,
serial->num_bulk_in,
serial->num_bulk_out,
serial->num_interrupt_in);
return -EINVAL;
}
return 0;
}
static int ark3116_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct ark3116_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
mutex_init(&priv->hw_lock);
spin_lock_init(&priv->status_lock);
priv->irda = is_irda(serial);
usb_set_serial_port_data(port, priv);
/* setup the hardware */
ark3116_write_reg(serial, UART_IER, 0);
/* disable DMA */
ark3116_write_reg(serial, UART_FCR, 0);
/* handshake control */
priv->hcr = 0;
ark3116_write_reg(serial, 0x8 , 0);
/* modem control */
priv->mcr = 0;
ark3116_write_reg(serial, UART_MCR, 0);
if (!(priv->irda)) {
ark3116_write_reg(serial, 0xb , 0);
} else {
ark3116_write_reg(serial, 0xb , 1);
ark3116_write_reg(serial, 0xc , 0);
ark3116_write_reg(serial, 0xd , 0x41);
ark3116_write_reg(serial, 0xa , 1);
}
/* setup baudrate */
ark3116_write_reg(serial, UART_LCR, UART_LCR_DLAB);
/* setup for 9600 8N1 */
priv->quot = calc_divisor(9600);
ark3116_write_reg(serial, UART_DLL, priv->quot & 0xff);
ark3116_write_reg(serial, UART_DLM, (priv->quot>>8) & 0xff);
priv->lcr = UART_LCR_WLEN8;
ark3116_write_reg(serial, UART_LCR, UART_LCR_WLEN8);
ark3116_write_reg(serial, 0xe, 0);
if (priv->irda)
ark3116_write_reg(serial, 0x9, 0);
dev_info(&serial->dev->dev,
"%s using %s mode\n",
KBUILD_MODNAME,
priv->irda ? "IrDA" : "RS232");
return 0;
}
static int ark3116_port_remove(struct usb_serial_port *port)
{
struct ark3116_private *priv = usb_get_serial_port_data(port);
/* device is closed, so URBs and DMA should be down */
mutex_destroy(&priv->hw_lock);
kfree(priv);
return 0;
}
static void ark3116_init_termios(struct tty_struct *tty)
{
struct ktermios *termios = &tty->termios;
*termios = tty_std_termios;
termios->c_cflag = B9600 | CS8
| CREAD | HUPCL | CLOCAL;
termios->c_ispeed = 9600;
termios->c_ospeed = 9600;
}
static void ark3116_set_termios(struct tty_struct *tty,
struct usb_serial_port *port,
struct ktermios *old_termios)
{
struct usb_serial *serial = port->serial;
struct ark3116_private *priv = usb_get_serial_port_data(port);
struct ktermios *termios = &tty->termios;
unsigned int cflag = termios->c_cflag;
int bps = tty_get_baud_rate(tty);
int quot;
__u8 lcr, hcr, eval;
/* set data bit count */
switch (cflag & CSIZE) {
case CS5:
lcr = UART_LCR_WLEN5;
break;
case CS6:
lcr = UART_LCR_WLEN6;
break;
case CS7:
lcr = UART_LCR_WLEN7;
break;
default:
case CS8:
lcr = UART_LCR_WLEN8;
break;
}
if (cflag & CSTOPB)
lcr |= UART_LCR_STOP;
if (cflag & PARENB)
lcr |= UART_LCR_PARITY;
if (!(cflag & PARODD))
lcr |= UART_LCR_EPAR;
#ifdef CMSPAR
if (cflag & CMSPAR)
lcr |= UART_LCR_SPAR;
#endif
/* handshake control */
hcr = (cflag & CRTSCTS) ? 0x03 : 0x00;
/* calc baudrate */
dev_dbg(&port->dev, "%s - setting bps to %d\n", __func__, bps);
eval = 0;
switch (bps) {
case 0:
quot = calc_divisor(9600);
break;
default:
if ((bps < 75) || (bps > 3000000))
bps = 9600;
quot = calc_divisor(bps);
break;
case 460800:
eval = 1;
quot = calc_divisor(bps);
break;
case 921600:
eval = 2;
quot = calc_divisor(bps);
break;
}
/* Update state: synchronize */
mutex_lock(&priv->hw_lock);
/* keep old LCR_SBC bit */
lcr |= (priv->lcr & UART_LCR_SBC);
dev_dbg(&port->dev, "%s - setting hcr:0x%02x,lcr:0x%02x,quot:%d\n",
__func__, hcr, lcr, quot);
/* handshake control */
if (priv->hcr != hcr) {
priv->hcr = hcr;
ark3116_write_reg(serial, 0x8, hcr);
}
/* baudrate */
if (priv->quot != quot) {
priv->quot = quot;
priv->lcr = lcr; /* need to write lcr anyway */
/* disable DMA since transmit/receive is
* shadowed by UART_DLL
*/
ark3116_write_reg(serial, UART_FCR, 0);
ark3116_write_reg(serial, UART_LCR,
lcr|UART_LCR_DLAB);
ark3116_write_reg(serial, UART_DLL, quot & 0xff);
ark3116_write_reg(serial, UART_DLM, (quot>>8) & 0xff);
/* restore lcr */
ark3116_write_reg(serial, UART_LCR, lcr);
/* magic baudrate thingy: not sure what it does,
* but windows does this as well.
*/
ark3116_write_reg(serial, 0xe, eval);
/* enable DMA */
ark3116_write_reg(serial, UART_FCR, UART_FCR_DMA_SELECT);
} else if (priv->lcr != lcr) {
priv->lcr = lcr;
ark3116_write_reg(serial, UART_LCR, lcr);
}
mutex_unlock(&priv->hw_lock);
/* check for software flow control */
if (I_IXOFF(tty) || I_IXON(tty)) {
dev_warn(&serial->dev->dev,
"%s: don't know how to do software flow control\n",
KBUILD_MODNAME);
}
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, bps, bps);
}
static void ark3116_close(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
/* disable DMA */
ark3116_write_reg(serial, UART_FCR, 0);
/* deactivate interrupts */
ark3116_write_reg(serial, UART_IER, 0);
usb_serial_generic_close(port);
if (serial->num_interrupt_in)
usb_kill_urb(port->interrupt_in_urb);
}
static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct ark3116_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
unsigned char *buf;
int result;
buf = kmalloc(1, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
result = usb_serial_generic_open(tty, port);
if (result) {
dev_dbg(&port->dev,
"%s - usb_serial_generic_open failed: %d\n",
__func__, result);
goto err_out;
}
/* remove any data still left: also clears error state */
ark3116_read_reg(serial, UART_RX, buf);
/* read modem status */
priv->msr = ark3116_read_reg(serial, UART_MSR, buf);
/* read line status */
priv->lsr = ark3116_read_reg(serial, UART_LSR, buf);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result) {
dev_err(&port->dev, "submit irq_in urb failed %d\n",
result);
ark3116_close(port);
goto err_out;
}
/* activate interrupts */
ark3116_write_reg(port->serial, UART_IER, UART_IER_MSI|UART_IER_RLSI);
/* enable DMA */
ark3116_write_reg(port->serial, UART_FCR, UART_FCR_DMA_SELECT);
/* setup termios */
if (tty)
ark3116_set_termios(tty, port, NULL);
err_out:
kfree(buf);
return result;
}
static int ark3116_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
struct serial_struct serstruct;
void __user *user_arg = (void __user *)arg;
switch (cmd) {
case TIOCGSERIAL:
/* XXX: Some of these values are probably wrong. */
memset(&serstruct, 0, sizeof(serstruct));
serstruct.type = PORT_16654;
serstruct.line = port->minor;
serstruct.port = port->port_number;
serstruct.custom_divisor = 0;
serstruct.baud_base = 460800;
if (copy_to_user(user_arg, &serstruct, sizeof(serstruct)))
return -EFAULT;
return 0;
case TIOCSSERIAL:
if (copy_from_user(&serstruct, user_arg, sizeof(serstruct)))
return -EFAULT;
return 0;
}
return -ENOIOCTLCMD;
}
static int ark3116_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct ark3116_private *priv = usb_get_serial_port_data(port);
__u32 status;
__u32 ctrl;
unsigned long flags;
mutex_lock(&priv->hw_lock);
ctrl = priv->mcr;
mutex_unlock(&priv->hw_lock);
spin_lock_irqsave(&priv->status_lock, flags);
status = priv->msr;
spin_unlock_irqrestore(&priv->status_lock, flags);
return (status & UART_MSR_DSR ? TIOCM_DSR : 0) |
(status & UART_MSR_CTS ? TIOCM_CTS : 0) |
(status & UART_MSR_RI ? TIOCM_RI : 0) |
(status & UART_MSR_DCD ? TIOCM_CD : 0) |
(ctrl & UART_MCR_DTR ? TIOCM_DTR : 0) |
(ctrl & UART_MCR_RTS ? TIOCM_RTS : 0) |
(ctrl & UART_MCR_OUT1 ? TIOCM_OUT1 : 0) |
(ctrl & UART_MCR_OUT2 ? TIOCM_OUT2 : 0);
}
static int ark3116_tiocmset(struct tty_struct *tty,
unsigned set, unsigned clr)
{
struct usb_serial_port *port = tty->driver_data;
struct ark3116_private *priv = usb_get_serial_port_data(port);
/* we need to take the mutex here, to make sure that the value
* in priv->mcr is actually the one that is in the hardware
*/
mutex_lock(&priv->hw_lock);
if (set & TIOCM_RTS)
priv->mcr |= UART_MCR_RTS;
if (set & TIOCM_DTR)
priv->mcr |= UART_MCR_DTR;
if (set & TIOCM_OUT1)
priv->mcr |= UART_MCR_OUT1;
if (set & TIOCM_OUT2)
priv->mcr |= UART_MCR_OUT2;
if (clr & TIOCM_RTS)
priv->mcr &= ~UART_MCR_RTS;
if (clr & TIOCM_DTR)
priv->mcr &= ~UART_MCR_DTR;
if (clr & TIOCM_OUT1)
priv->mcr &= ~UART_MCR_OUT1;
if (clr & TIOCM_OUT2)
priv->mcr &= ~UART_MCR_OUT2;
ark3116_write_reg(port->serial, UART_MCR, priv->mcr);
mutex_unlock(&priv->hw_lock);
return 0;
}
static void ark3116_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct ark3116_private *priv = usb_get_serial_port_data(port);
/* LCR is also used for other things: protect access */
mutex_lock(&priv->hw_lock);
if (break_state)
priv->lcr |= UART_LCR_SBC;
else
priv->lcr &= ~UART_LCR_SBC;
ark3116_write_reg(port->serial, UART_LCR, priv->lcr);
mutex_unlock(&priv->hw_lock);
}
static void ark3116_update_msr(struct usb_serial_port *port, __u8 msr)
{
struct ark3116_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
spin_lock_irqsave(&priv->status_lock, flags);
priv->msr = msr;
spin_unlock_irqrestore(&priv->status_lock, flags);
if (msr & UART_MSR_ANY_DELTA) {
/* update input line counters */
if (msr & UART_MSR_DCTS)
port->icount.cts++;
if (msr & UART_MSR_DDSR)
port->icount.dsr++;
if (msr & UART_MSR_DDCD)
port->icount.dcd++;
if (msr & UART_MSR_TERI)
port->icount.rng++;
wake_up_interruptible(&port->port.delta_msr_wait);
}
}
static void ark3116_update_lsr(struct usb_serial_port *port, __u8 lsr)
{
struct ark3116_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
spin_lock_irqsave(&priv->status_lock, flags);
/* combine bits */
priv->lsr |= lsr;
spin_unlock_irqrestore(&priv->status_lock, flags);
if (lsr&UART_LSR_BRK_ERROR_BITS) {
if (lsr & UART_LSR_BI)
port->icount.brk++;
if (lsr & UART_LSR_FE)
port->icount.frame++;
if (lsr & UART_LSR_PE)
port->icount.parity++;
if (lsr & UART_LSR_OE)
port->icount.overrun++;
}
}
static void ark3116_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
int status = urb->status;
const __u8 *data = urb->transfer_buffer;
int result;
switch (status) {
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
__func__, status);
break;
case 0: /* success */
/* discovered this by trail and error... */
if ((urb->actual_length == 4) && (data[0] == 0xe8)) {
const __u8 id = data[1]&UART_IIR_ID;
dev_dbg(&port->dev, "%s: iir=%02x\n", __func__, data[1]);
if (id == UART_IIR_MSI) {
dev_dbg(&port->dev, "%s: msr=%02x\n",
__func__, data[3]);
ark3116_update_msr(port, data[3]);
break;
} else if (id == UART_IIR_RLSI) {
dev_dbg(&port->dev, "%s: lsr=%02x\n",
__func__, data[2]);
ark3116_update_lsr(port, data[2]);
break;
}
}
/*
* Not sure what this data meant...
*/
usb_serial_debug_data(&port->dev, __func__,
urb->actual_length,
urb->transfer_buffer);
break;
}
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
dev_err(&urb->dev->dev,
"%s - Error %d submitting interrupt urb\n",
__func__, result);
}
/* Data comes in via the bulk (data) URB, errors/interrupts via the int URB.
* This means that we cannot be sure which data byte has an associated error
* condition, so we report an error for all data in the next bulk read.
*
* Actually, there might even be a window between the bulk data leaving the
* ark and reading/resetting the lsr in the read_bulk_callback where an
* interrupt for the next data block could come in.
* Without somekind of ordering on the ark, we would have to report the
* error for the next block of data as well...
* For now, let's pretend this can't happen.
*/
static void ark3116_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct ark3116_private *priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
char tty_flag = TTY_NORMAL;
unsigned long flags;
__u32 lsr;
/* update line status */
spin_lock_irqsave(&priv->status_lock, flags);
lsr = priv->lsr;
priv->lsr &= ~UART_LSR_BRK_ERROR_BITS;
spin_unlock_irqrestore(&priv->status_lock, flags);
if (!urb->actual_length)
return;
if (lsr & UART_LSR_BRK_ERROR_BITS) {
if (lsr & UART_LSR_BI)
tty_flag = TTY_BREAK;
else if (lsr & UART_LSR_PE)
tty_flag = TTY_PARITY;
else if (lsr & UART_LSR_FE)
tty_flag = TTY_FRAME;
/* overrun is special, not associated with a char */
if (lsr & UART_LSR_OE)
tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
}
tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag,
urb->actual_length);
tty_flip_buffer_push(&port->port);
}
static struct usb_serial_driver ark3116_device = {
.driver = {
.owner = THIS_MODULE,
.name = "ark3116",
},
.id_table = id_table,
.num_ports = 1,
.attach = ark3116_attach,
.port_probe = ark3116_port_probe,
.port_remove = ark3116_port_remove,
.set_termios = ark3116_set_termios,
.init_termios = ark3116_init_termios,
.ioctl = ark3116_ioctl,
.tiocmget = ark3116_tiocmget,
.tiocmset = ark3116_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.open = ark3116_open,
.close = ark3116_close,
.break_ctl = ark3116_break_ctl,
.read_int_callback = ark3116_read_int_callback,
.process_read_urb = ark3116_process_read_urb,
};
static struct usb_serial_driver * const serial_drivers[] = {
&ark3116_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
/*
* The following describes what I learned from studying the old
* ark3116.c driver, disassembling the windows driver, and some lucky
* guesses. Since I do not have any datasheet or other
* documentation, inaccuracies are almost guaranteed.
*
* Some specs for the ARK3116 can be found here:
* http://web.archive.org/web/20060318000438/
* www.arkmicro.com/en/products/view.php?id=10
* On that page, 2 GPIO pins are mentioned: I assume these are the
* OUT1 and OUT2 pins of the UART, so I added support for those
* through the MCR. Since the pins are not available on my hardware,
* I could not verify this.
* Also, it states there is "on-chip hardware flow control". I have
* discovered how to enable that. Unfortunately, I do not know how to
* enable XON/XOFF (software) flow control, which would need support
* from the chip as well to work. Because of the wording on the web
* page there is a real possibility the chip simply does not support
* software flow control.
*
* I got my ark3116 as part of a mobile phone adapter cable. On the
* PCB, the following numbered contacts are present:
*
* 1:- +5V
* 2:o DTR
* 3:i RX
* 4:i DCD
* 5:o RTS
* 6:o TX
* 7:i RI
* 8:i DSR
* 10:- 0V
* 11:i CTS
*
* On my chip, all signals seem to be 3.3V, but 5V tolerant. But that
* may be different for the one you have ;-).
*
* The windows driver limits the registers to 0-F, so I assume there
* are actually 16 present on the device.
*
* On an UART interrupt, 4 bytes of data come in on the interrupt
* endpoint. The bytes are 0xe8 IIR LSR MSR.
*
* The baudrate seems to be generated from the 12MHz crystal, using
* 4-times subsampling. So quot=12e6/(4*baud). Also see description
* of register E.
*
* Registers 0-7:
* These seem to be the same as for a regular 16450. The FCR is set
* to UART_FCR_DMA_SELECT (0x8), I guess to enable transfers between
* the UART and the USB bridge/DMA engine.
*
* Register 8:
* By trial and error, I found out that bit 0 enables hardware CTS,
* stopping TX when CTS is +5V. Bit 1 does the same for RTS, making
* RTS +5V when the 3116 cannot transfer the data to the USB bus
* (verified by disabling the reading URB). Note that as far as I can
* tell, the windows driver does NOT use this, so there might be some
* hardware bug or something.
*
* According to a patch provided here
* (http://lkml.org/lkml/2009/7/26/56), the ARK3116 can also be used
* as an IrDA dongle. Since I do not have such a thing, I could not
* investigate that aspect. However, I can speculate ;-).
*
* - IrDA encodes data differently than RS232. Most likely, one of
* the bits in registers 9..E enables the IR ENDEC (encoder/decoder).
* - Depending on the IR transceiver, the input and output need to be
* inverted, so there are probably bits for that as well.
* - IrDA is half-duplex, so there should be a bit for selecting that.
*
* This still leaves at least two registers unaccounted for. Perhaps
* The chip can do XON/XOFF or CRC in HW?
*
* Register 9:
* Set to 0x00 for IrDA, when the baudrate is initialised.
*
* Register A:
* Set to 0x01 for IrDA, at init.
*
* Register B:
* Set to 0x01 for IrDA, 0x00 for RS232, at init.
*
* Register C:
* Set to 00 for IrDA, at init.
*
* Register D:
* Set to 0x41 for IrDA, at init.
*
* Register E:
* Somekind of baudrate override. The windows driver seems to set
* this to 0x00 for normal baudrates, 0x01 for 460800, 0x02 for 921600.
* Since 460800 and 921600 cannot be obtained by dividing 3MHz by an integer,
* it could be somekind of subdivisor thingy.
* However,it does not seem to do anything: selecting 921600 (divisor 3,
* reg E=2), still gets 1 MHz. I also checked if registers 9, C or F would
* work, but they don't.
*
* Register F: unknown
*/

View file

@ -0,0 +1,501 @@
/*
* Belkin USB Serial Adapter Driver
*
* Copyright (C) 2000 William Greathouse (wgreathouse@smva.com)
* Copyright (C) 2000-2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2010 Johan Hovold (jhovold@gmail.com)
*
* This program is largely derived from work by the linux-usb group
* and associated source files. Please see the usb/serial files for
* individual credits and copyrights.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver
*
* TODO:
* -- Add true modem control line query capability. Currently we track the
* states reported by the interrupt and the states we request.
* -- Add support for flush commands
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include "belkin_sa.h"
#define DRIVER_AUTHOR "William Greathouse <wgreathouse@smva.com>"
#define DRIVER_DESC "USB Belkin Serial converter driver"
/* function prototypes for a Belkin USB Serial Adapter F5U103 */
static int belkin_sa_port_probe(struct usb_serial_port *port);
static int belkin_sa_port_remove(struct usb_serial_port *port);
static int belkin_sa_open(struct tty_struct *tty,
struct usb_serial_port *port);
static void belkin_sa_close(struct usb_serial_port *port);
static void belkin_sa_read_int_callback(struct urb *urb);
static void belkin_sa_process_read_urb(struct urb *urb);
static void belkin_sa_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios * old);
static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state);
static int belkin_sa_tiocmget(struct tty_struct *tty);
static int belkin_sa_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(BELKIN_SA_VID, BELKIN_SA_PID) },
{ USB_DEVICE(BELKIN_OLD_VID, BELKIN_OLD_PID) },
{ USB_DEVICE(PERACOM_VID, PERACOM_PID) },
{ USB_DEVICE(GOHUBS_VID, GOHUBS_PID) },
{ USB_DEVICE(GOHUBS_VID, HANDYLINK_PID) },
{ USB_DEVICE(BELKIN_DOCKSTATION_VID, BELKIN_DOCKSTATION_PID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
/* All of the device info needed for the serial converters */
static struct usb_serial_driver belkin_device = {
.driver = {
.owner = THIS_MODULE,
.name = "belkin",
},
.description = "Belkin / Peracom / GoHubs USB Serial Adapter",
.id_table = id_table,
.num_ports = 1,
.open = belkin_sa_open,
.close = belkin_sa_close,
.read_int_callback = belkin_sa_read_int_callback,
.process_read_urb = belkin_sa_process_read_urb,
.set_termios = belkin_sa_set_termios,
.break_ctl = belkin_sa_break_ctl,
.tiocmget = belkin_sa_tiocmget,
.tiocmset = belkin_sa_tiocmset,
.port_probe = belkin_sa_port_probe,
.port_remove = belkin_sa_port_remove,
};
static struct usb_serial_driver * const serial_drivers[] = {
&belkin_device, NULL
};
struct belkin_sa_private {
spinlock_t lock;
unsigned long control_state;
unsigned char last_lsr;
unsigned char last_msr;
int bad_flow_control;
};
/*
* ***************************************************************************
* Belkin USB Serial Adapter F5U103 specific driver functions
* ***************************************************************************
*/
#define WDR_TIMEOUT 5000 /* default urb timeout */
/* assumes that struct usb_serial *serial is available */
#define BSA_USB_CMD(c, v) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \
(c), BELKIN_SA_SET_REQUEST_TYPE, \
(v), 0, NULL, 0, WDR_TIMEOUT)
static int belkin_sa_port_probe(struct usb_serial_port *port)
{
struct usb_device *dev = port->serial->dev;
struct belkin_sa_private *priv;
priv = kmalloc(sizeof(struct belkin_sa_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
priv->control_state = 0;
priv->last_lsr = 0;
priv->last_msr = 0;
/* see comments at top of file */
priv->bad_flow_control =
(le16_to_cpu(dev->descriptor.bcdDevice) <= 0x0206) ? 1 : 0;
dev_info(&dev->dev, "bcdDevice: %04x, bfc: %d\n",
le16_to_cpu(dev->descriptor.bcdDevice),
priv->bad_flow_control);
usb_set_serial_port_data(port, priv);
return 0;
}
static int belkin_sa_port_remove(struct usb_serial_port *port)
{
struct belkin_sa_private *priv;
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static int belkin_sa_open(struct tty_struct *tty,
struct usb_serial_port *port)
{
int retval;
retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (retval) {
dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
return retval;
}
retval = usb_serial_generic_open(tty, port);
if (retval)
usb_kill_urb(port->interrupt_in_urb);
return retval;
}
static void belkin_sa_close(struct usb_serial_port *port)
{
usb_serial_generic_close(port);
usb_kill_urb(port->interrupt_in_urb);
}
static void belkin_sa_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct belkin_sa_private *priv;
unsigned char *data = urb->transfer_buffer;
int retval;
int status = urb->status;
unsigned long flags;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
__func__, status);
goto exit;
}
usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
/* Handle known interrupt data */
/* ignore data[0] and data[1] */
priv = usb_get_serial_port_data(port);
spin_lock_irqsave(&priv->lock, flags);
priv->last_msr = data[BELKIN_SA_MSR_INDEX];
/* Record Control Line states */
if (priv->last_msr & BELKIN_SA_MSR_DSR)
priv->control_state |= TIOCM_DSR;
else
priv->control_state &= ~TIOCM_DSR;
if (priv->last_msr & BELKIN_SA_MSR_CTS)
priv->control_state |= TIOCM_CTS;
else
priv->control_state &= ~TIOCM_CTS;
if (priv->last_msr & BELKIN_SA_MSR_RI)
priv->control_state |= TIOCM_RI;
else
priv->control_state &= ~TIOCM_RI;
if (priv->last_msr & BELKIN_SA_MSR_CD)
priv->control_state |= TIOCM_CD;
else
priv->control_state &= ~TIOCM_CD;
priv->last_lsr = data[BELKIN_SA_LSR_INDEX];
spin_unlock_irqrestore(&priv->lock, flags);
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
dev_err(&port->dev, "%s - usb_submit_urb failed with "
"result %d\n", __func__, retval);
}
static void belkin_sa_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct belkin_sa_private *priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
unsigned long flags;
unsigned char status;
char tty_flag;
/* Update line status */
tty_flag = TTY_NORMAL;
spin_lock_irqsave(&priv->lock, flags);
status = priv->last_lsr;
priv->last_lsr &= ~BELKIN_SA_LSR_ERR;
spin_unlock_irqrestore(&priv->lock, flags);
if (!urb->actual_length)
return;
if (status & BELKIN_SA_LSR_ERR) {
/* Break takes precedence over parity, which takes precedence
* over framing errors. */
if (status & BELKIN_SA_LSR_BI)
tty_flag = TTY_BREAK;
else if (status & BELKIN_SA_LSR_PE)
tty_flag = TTY_PARITY;
else if (status & BELKIN_SA_LSR_FE)
tty_flag = TTY_FRAME;
dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag);
/* Overrun is special, not associated with a char. */
if (status & BELKIN_SA_LSR_OE)
tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
}
tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag,
urb->actual_length);
tty_flip_buffer_push(&port->port);
}
static void belkin_sa_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct usb_serial *serial = port->serial;
struct belkin_sa_private *priv = usb_get_serial_port_data(port);
unsigned int iflag;
unsigned int cflag;
unsigned int old_iflag = 0;
unsigned int old_cflag = 0;
__u16 urb_value = 0; /* Will hold the new flags */
unsigned long flags;
unsigned long control_state;
int bad_flow_control;
speed_t baud;
struct ktermios *termios = &tty->termios;
iflag = termios->c_iflag;
cflag = termios->c_cflag;
termios->c_cflag &= ~CMSPAR;
/* get a local copy of the current port settings */
spin_lock_irqsave(&priv->lock, flags);
control_state = priv->control_state;
bad_flow_control = priv->bad_flow_control;
spin_unlock_irqrestore(&priv->lock, flags);
old_iflag = old_termios->c_iflag;
old_cflag = old_termios->c_cflag;
/* Set the baud rate */
if ((cflag & CBAUD) != (old_cflag & CBAUD)) {
/* reassert DTR and (maybe) RTS on transition from B0 */
if ((old_cflag & CBAUD) == B0) {
control_state |= (TIOCM_DTR|TIOCM_RTS);
if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 1) < 0)
dev_err(&port->dev, "Set DTR error\n");
/* don't set RTS if using hardware flow control */
if (!(old_cflag & CRTSCTS))
if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST
, 1) < 0)
dev_err(&port->dev, "Set RTS error\n");
}
}
baud = tty_get_baud_rate(tty);
if (baud) {
urb_value = BELKIN_SA_BAUD(baud);
/* Clip to maximum speed */
if (urb_value == 0)
urb_value = 1;
/* Turn it back into a resulting real baud rate */
baud = BELKIN_SA_BAUD(urb_value);
/* Report the actual baud rate back to the caller */
tty_encode_baud_rate(tty, baud, baud);
if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0)
dev_err(&port->dev, "Set baudrate error\n");
} else {
/* Disable flow control */
if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST,
BELKIN_SA_FLOW_NONE) < 0)
dev_err(&port->dev, "Disable flowcontrol error\n");
/* Drop RTS and DTR */
control_state &= ~(TIOCM_DTR | TIOCM_RTS);
if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0)
dev_err(&port->dev, "DTR LOW error\n");
if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0)
dev_err(&port->dev, "RTS LOW error\n");
}
/* set the parity */
if ((cflag ^ old_cflag) & (PARENB | PARODD)) {
if (cflag & PARENB)
urb_value = (cflag & PARODD) ? BELKIN_SA_PARITY_ODD
: BELKIN_SA_PARITY_EVEN;
else
urb_value = BELKIN_SA_PARITY_NONE;
if (BSA_USB_CMD(BELKIN_SA_SET_PARITY_REQUEST, urb_value) < 0)
dev_err(&port->dev, "Set parity error\n");
}
/* set the number of data bits */
if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
switch (cflag & CSIZE) {
case CS5:
urb_value = BELKIN_SA_DATA_BITS(5);
break;
case CS6:
urb_value = BELKIN_SA_DATA_BITS(6);
break;
case CS7:
urb_value = BELKIN_SA_DATA_BITS(7);
break;
case CS8:
urb_value = BELKIN_SA_DATA_BITS(8);
break;
default:
dev_dbg(&port->dev,
"CSIZE was not CS5-CS8, using default of 8\n");
urb_value = BELKIN_SA_DATA_BITS(8);
break;
}
if (BSA_USB_CMD(BELKIN_SA_SET_DATA_BITS_REQUEST, urb_value) < 0)
dev_err(&port->dev, "Set data bits error\n");
}
/* set the number of stop bits */
if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
urb_value = (cflag & CSTOPB) ? BELKIN_SA_STOP_BITS(2)
: BELKIN_SA_STOP_BITS(1);
if (BSA_USB_CMD(BELKIN_SA_SET_STOP_BITS_REQUEST,
urb_value) < 0)
dev_err(&port->dev, "Set stop bits error\n");
}
/* Set flow control */
if (((iflag ^ old_iflag) & (IXOFF | IXON)) ||
((cflag ^ old_cflag) & CRTSCTS)) {
urb_value = 0;
if ((iflag & IXOFF) || (iflag & IXON))
urb_value |= (BELKIN_SA_FLOW_OXON | BELKIN_SA_FLOW_IXON);
else
urb_value &= ~(BELKIN_SA_FLOW_OXON | BELKIN_SA_FLOW_IXON);
if (cflag & CRTSCTS)
urb_value |= (BELKIN_SA_FLOW_OCTS | BELKIN_SA_FLOW_IRTS);
else
urb_value &= ~(BELKIN_SA_FLOW_OCTS | BELKIN_SA_FLOW_IRTS);
if (bad_flow_control)
urb_value &= ~(BELKIN_SA_FLOW_IRTS);
if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, urb_value) < 0)
dev_err(&port->dev, "Set flow control error\n");
}
/* save off the modified port settings */
spin_lock_irqsave(&priv->lock, flags);
priv->control_state = control_state;
spin_unlock_irqrestore(&priv->lock, flags);
}
static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
if (BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0) < 0)
dev_err(&port->dev, "Set break_ctl %d\n", break_state);
}
static int belkin_sa_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct belkin_sa_private *priv = usb_get_serial_port_data(port);
unsigned long control_state;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
control_state = priv->control_state;
spin_unlock_irqrestore(&priv->lock, flags);
return control_state;
}
static int belkin_sa_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
struct belkin_sa_private *priv = usb_get_serial_port_data(port);
unsigned long control_state;
unsigned long flags;
int retval;
int rts = 0;
int dtr = 0;
spin_lock_irqsave(&priv->lock, flags);
control_state = priv->control_state;
if (set & TIOCM_RTS) {
control_state |= TIOCM_RTS;
rts = 1;
}
if (set & TIOCM_DTR) {
control_state |= TIOCM_DTR;
dtr = 1;
}
if (clear & TIOCM_RTS) {
control_state &= ~TIOCM_RTS;
rts = 0;
}
if (clear & TIOCM_DTR) {
control_state &= ~TIOCM_DTR;
dtr = 0;
}
priv->control_state = control_state;
spin_unlock_irqrestore(&priv->lock, flags);
retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, rts);
if (retval < 0) {
dev_err(&port->dev, "Set RTS error %d\n", retval);
goto exit;
}
retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, dtr);
if (retval < 0) {
dev_err(&port->dev, "Set DTR error %d\n", retval);
goto exit;
}
exit:
return retval;
}
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,124 @@
/*
* Definitions for Belkin USB Serial Adapter Driver
*
* Copyright (C) 2000
* William Greathouse (wgreathouse@smva.com)
*
* This program is largely derived from work by the linux-usb group
* and associated source files. Please see the usb/serial files for
* individual credits and copyrights.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver
*
* 12-Mar-2001 gkh
* Added GoHubs GO-COM232 device id.
*
* 06-Nov-2000 gkh
* Added old Belkin and Peracom device ids, which this driver supports
*
* 12-Oct-2000 William Greathouse
* First cut at supporting Belkin USB Serial Adapter F5U103
* I did not have a copy of the original work to support this
* adapter, so pardon any stupid mistakes. All of the information
* I am using to write this driver was acquired by using a modified
* UsbSnoop on Windows2000.
*
*/
#ifndef __LINUX_USB_SERIAL_BSA_H
#define __LINUX_USB_SERIAL_BSA_H
#define BELKIN_DOCKSTATION_VID 0x050d /* Vendor Id */
#define BELKIN_DOCKSTATION_PID 0x1203 /* Product Id */
#define BELKIN_SA_VID 0x050d /* Vendor Id */
#define BELKIN_SA_PID 0x0103 /* Product Id */
#define BELKIN_OLD_VID 0x056c /* Belkin's "old" vendor id */
#define BELKIN_OLD_PID 0x8007 /* Belkin's "old" single port serial converter's id */
#define PERACOM_VID 0x0565 /* Peracom's vendor id */
#define PERACOM_PID 0x0001 /* Peracom's single port serial converter's id */
#define GOHUBS_VID 0x0921 /* GoHubs vendor id */
#define GOHUBS_PID 0x1000 /* GoHubs single port serial converter's id (identical to the Peracom device) */
#define HANDYLINK_PID 0x1200 /* HandyLink USB's id (identical to the Peracom device) */
/* Vendor Request Interface */
#define BELKIN_SA_SET_BAUDRATE_REQUEST 0 /* Set baud rate */
#define BELKIN_SA_SET_STOP_BITS_REQUEST 1 /* Set stop bits (1,2) */
#define BELKIN_SA_SET_DATA_BITS_REQUEST 2 /* Set data bits (5,6,7,8) */
#define BELKIN_SA_SET_PARITY_REQUEST 3 /* Set parity (None, Even, Odd) */
#define BELKIN_SA_SET_DTR_REQUEST 10 /* Set DTR state */
#define BELKIN_SA_SET_RTS_REQUEST 11 /* Set RTS state */
#define BELKIN_SA_SET_BREAK_REQUEST 12 /* Set BREAK state */
#define BELKIN_SA_SET_FLOW_CTRL_REQUEST 16 /* Set flow control mode */
#ifdef WHEN_I_LEARN_THIS
#define BELKIN_SA_SET_MAGIC_REQUEST 17 /* I don't know, possibly flush */
/* (always in Wininit sequence before flow control) */
#define BELKIN_SA_RESET xx /* Reset the port */
#define BELKIN_SA_GET_MODEM_STATUS xx /* Force return of modem status register */
#endif
#define BELKIN_SA_SET_REQUEST_TYPE 0x40
#define BELKIN_SA_BAUD(b) (230400/b)
#define BELKIN_SA_STOP_BITS(b) (b-1)
#define BELKIN_SA_DATA_BITS(b) (b-5)
#define BELKIN_SA_PARITY_NONE 0
#define BELKIN_SA_PARITY_EVEN 1
#define BELKIN_SA_PARITY_ODD 2
#define BELKIN_SA_PARITY_MARK 3
#define BELKIN_SA_PARITY_SPACE 4
#define BELKIN_SA_FLOW_NONE 0x0000 /* No flow control */
#define BELKIN_SA_FLOW_OCTS 0x0001 /* use CTS input to throttle output */
#define BELKIN_SA_FLOW_ODSR 0x0002 /* use DSR input to throttle output */
#define BELKIN_SA_FLOW_IDSR 0x0004 /* use DSR input to enable receive */
#define BELKIN_SA_FLOW_IDTR 0x0008 /* use DTR output for input flow control */
#define BELKIN_SA_FLOW_IRTS 0x0010 /* use RTS output for input flow control */
#define BELKIN_SA_FLOW_ORTS 0x0020 /* use RTS to indicate data available to send */
#define BELKIN_SA_FLOW_ERRSUB 0x0040 /* ???? guess ???? substitute inline errors */
#define BELKIN_SA_FLOW_OXON 0x0080 /* use XON/XOFF for output flow control */
#define BELKIN_SA_FLOW_IXON 0x0100 /* use XON/XOFF for input flow control */
/*
* It seems that the interrupt pipe is closely modelled after the
* 16550 register layout. This is probably because the adapter can
* be used in a "DOS" environment to simulate a standard hardware port.
*/
#define BELKIN_SA_LSR_INDEX 2 /* Line Status Register */
#define BELKIN_SA_LSR_RDR 0x01 /* receive data ready */
#define BELKIN_SA_LSR_OE 0x02 /* overrun error */
#define BELKIN_SA_LSR_PE 0x04 /* parity error */
#define BELKIN_SA_LSR_FE 0x08 /* framing error */
#define BELKIN_SA_LSR_BI 0x10 /* break indicator */
#define BELKIN_SA_LSR_THE 0x20 /* tx holding register empty */
#define BELKIN_SA_LSR_TE 0x40 /* transmit register empty */
#define BELKIN_SA_LSR_ERR 0x80 /* OE | PE | FE | BI */
#define BELKIN_SA_MSR_INDEX 3 /* Modem Status Register */
#define BELKIN_SA_MSR_DCTS 0x01 /* Delta CTS */
#define BELKIN_SA_MSR_DDSR 0x02 /* Delta DSR */
#define BELKIN_SA_MSR_DRI 0x04 /* Delta RI */
#define BELKIN_SA_MSR_DCD 0x08 /* Delta CD */
#define BELKIN_SA_MSR_CTS 0x10 /* Current CTS */
#define BELKIN_SA_MSR_DSR 0x20 /* Current DSR */
#define BELKIN_SA_MSR_RI 0x40 /* Current RI */
#define BELKIN_SA_MSR_CD 0x80 /* Current CD */
#endif /* __LINUX_USB_SERIAL_BSA_H */

208
drivers/usb/serial/bus.c Normal file
View file

@ -0,0 +1,208 @@
/*
* USB Serial Converter Bus specific functions
*
* Copyright (C) 2002 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
static int usb_serial_device_match(struct device *dev,
struct device_driver *drv)
{
struct usb_serial_driver *driver;
const struct usb_serial_port *port;
/*
* drivers are already assigned to ports in serial_probe so it's
* a simple check here.
*/
port = to_usb_serial_port(dev);
if (!port)
return 0;
driver = to_usb_serial_driver(drv);
if (driver == port->serial->type)
return 1;
return 0;
}
static ssize_t port_number_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_serial_port *port = to_usb_serial_port(dev);
return sprintf(buf, "%d\n", port->port_number);
}
static DEVICE_ATTR_RO(port_number);
static int usb_serial_device_probe(struct device *dev)
{
struct usb_serial_driver *driver;
struct usb_serial_port *port;
struct device *tty_dev;
int retval = 0;
int minor;
port = to_usb_serial_port(dev);
if (!port) {
retval = -ENODEV;
goto exit;
}
/* make sure suspend/resume doesn't race against port_probe */
retval = usb_autopm_get_interface(port->serial->interface);
if (retval)
goto exit;
driver = port->serial->type;
if (driver->port_probe) {
retval = driver->port_probe(port);
if (retval)
goto exit_with_autopm;
}
retval = device_create_file(dev, &dev_attr_port_number);
if (retval) {
if (driver->port_remove)
driver->port_remove(port);
goto exit_with_autopm;
}
minor = port->minor;
tty_dev = tty_register_device(usb_serial_tty_driver, minor, dev);
if (IS_ERR(tty_dev)) {
retval = PTR_ERR(tty_dev);
device_remove_file(dev, &dev_attr_port_number);
if (driver->port_remove)
driver->port_remove(port);
goto exit_with_autopm;
}
dev_info(&port->serial->dev->dev,
"%s converter now attached to ttyUSB%d\n",
driver->description, minor);
exit_with_autopm:
usb_autopm_put_interface(port->serial->interface);
exit:
return retval;
}
static int usb_serial_device_remove(struct device *dev)
{
struct usb_serial_driver *driver;
struct usb_serial_port *port;
int retval = 0;
int minor;
int autopm_err;
port = to_usb_serial_port(dev);
if (!port)
return -ENODEV;
/*
* Make sure suspend/resume doesn't race against port_remove.
*
* Note that no further runtime PM callbacks will be made if
* autopm_get fails.
*/
autopm_err = usb_autopm_get_interface(port->serial->interface);
minor = port->minor;
tty_unregister_device(usb_serial_tty_driver, minor);
device_remove_file(&port->dev, &dev_attr_port_number);
driver = port->serial->type;
if (driver->port_remove)
retval = driver->port_remove(port);
dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
driver->description, minor);
if (!autopm_err)
usb_autopm_put_interface(port->serial->interface);
return retval;
}
static ssize_t new_id_store(struct device_driver *driver,
const char *buf, size_t count)
{
struct usb_serial_driver *usb_drv = to_usb_serial_driver(driver);
ssize_t retval = usb_store_new_id(&usb_drv->dynids, usb_drv->id_table,
driver, buf, count);
if (retval >= 0 && usb_drv->usb_driver != NULL)
retval = usb_store_new_id(&usb_drv->usb_driver->dynids,
usb_drv->usb_driver->id_table,
&usb_drv->usb_driver->drvwrap.driver,
buf, count);
return retval;
}
static ssize_t new_id_show(struct device_driver *driver, char *buf)
{
struct usb_serial_driver *usb_drv = to_usb_serial_driver(driver);
return usb_show_dynids(&usb_drv->dynids, buf);
}
static DRIVER_ATTR_RW(new_id);
static struct attribute *usb_serial_drv_attrs[] = {
&driver_attr_new_id.attr,
NULL,
};
ATTRIBUTE_GROUPS(usb_serial_drv);
static void free_dynids(struct usb_serial_driver *drv)
{
struct usb_dynid *dynid, *n;
spin_lock(&drv->dynids.lock);
list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
list_del(&dynid->node);
kfree(dynid);
}
spin_unlock(&drv->dynids.lock);
}
struct bus_type usb_serial_bus_type = {
.name = "usb-serial",
.match = usb_serial_device_match,
.probe = usb_serial_device_probe,
.remove = usb_serial_device_remove,
.drv_groups = usb_serial_drv_groups,
};
int usb_serial_bus_register(struct usb_serial_driver *driver)
{
int retval;
driver->driver.bus = &usb_serial_bus_type;
spin_lock_init(&driver->dynids.lock);
INIT_LIST_HEAD(&driver->dynids.list);
retval = driver_register(&driver->driver);
return retval;
}
void usb_serial_bus_deregister(struct usb_serial_driver *driver)
{
free_dynids(driver);
driver_unregister(&driver->driver);
}

584
drivers/usb/serial/ch341.c Normal file
View file

@ -0,0 +1,584 @@
/*
* Copyright 2007, Frank A Kingswood <frank@kingswood-consulting.co.uk>
* Copyright 2007, Werner Cornelius <werner@cornelius-consult.de>
* Copyright 2009, Boris Hajduk <boris@hajduk.org>
*
* ch341.c implements a serial port driver for the Winchiphead CH341.
*
* The CH341 device can be used to implement an RS232 asynchronous
* serial port, an IEEE-1284 parallel printer port or a memory-like
* interface. In all cases the CH341 supports an I2C interface as well.
* This driver only supports the asynchronous serial interface.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial.h>
#include <asm/unaligned.h>
#define DEFAULT_BAUD_RATE 9600
#define DEFAULT_TIMEOUT 1000
/* flags for IO-Bits */
#define CH341_BIT_RTS (1 << 6)
#define CH341_BIT_DTR (1 << 5)
/******************************/
/* interrupt pipe definitions */
/******************************/
/* always 4 interrupt bytes */
/* first irq byte normally 0x08 */
/* second irq byte base 0x7d + below */
/* third irq byte base 0x94 + below */
/* fourth irq byte normally 0xee */
/* second interrupt byte */
#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */
/* status returned in third interrupt answer byte, inverted in data
from irq */
#define CH341_BIT_CTS 0x01
#define CH341_BIT_DSR 0x02
#define CH341_BIT_RI 0x04
#define CH341_BIT_DCD 0x08
#define CH341_BITS_MODEM_STAT 0x0f /* all bits */
/*******************************/
/* baudrate calculation factor */
/*******************************/
#define CH341_BAUDBASE_FACTOR 1532620800
#define CH341_BAUDBASE_DIVMAX 3
/* Break support - the information used to implement this was gleaned from
* the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato.
*/
#define CH341_REQ_WRITE_REG 0x9A
#define CH341_REQ_READ_REG 0x95
#define CH341_REG_BREAK1 0x05
#define CH341_REG_BREAK2 0x18
#define CH341_NBREAK_BITS_REG1 0x01
#define CH341_NBREAK_BITS_REG2 0x40
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x4348, 0x5523) },
{ USB_DEVICE(0x1a86, 0x7523) },
{ USB_DEVICE(0x1a86, 0x5523) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
struct ch341_private {
spinlock_t lock; /* access lock */
unsigned baud_rate; /* set baud rate */
u8 line_control; /* set line control value RTS/DTR */
u8 line_status; /* active status of modem control inputs */
};
static int ch341_control_out(struct usb_device *dev, u8 request,
u16 value, u16 index)
{
int r;
dev_dbg(&dev->dev, "ch341_control_out(%02x,%02x,%04x,%04x)\n",
USB_DIR_OUT|0x40, (int)request, (int)value, (int)index);
r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
value, index, NULL, 0, DEFAULT_TIMEOUT);
return r;
}
static int ch341_control_in(struct usb_device *dev,
u8 request, u16 value, u16 index,
char *buf, unsigned bufsize)
{
int r;
dev_dbg(&dev->dev, "ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)\n",
USB_DIR_IN|0x40, (int)request, (int)value, (int)index, buf,
(int)bufsize);
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
value, index, buf, bufsize, DEFAULT_TIMEOUT);
return r;
}
static int ch341_set_baudrate(struct usb_device *dev,
struct ch341_private *priv)
{
short a, b;
int r;
unsigned long factor;
short divisor;
if (!priv->baud_rate)
return -EINVAL;
factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate);
divisor = CH341_BAUDBASE_DIVMAX;
while ((factor > 0xfff0) && divisor) {
factor >>= 3;
divisor--;
}
if (factor > 0xfff0)
return -EINVAL;
factor = 0x10000 - factor;
a = (factor & 0xff00) | divisor;
b = factor & 0xff;
r = ch341_control_out(dev, 0x9a, 0x1312, a);
if (!r)
r = ch341_control_out(dev, 0x9a, 0x0f2c, b);
return r;
}
static int ch341_set_handshake(struct usb_device *dev, u8 control)
{
return ch341_control_out(dev, 0xa4, ~control, 0);
}
static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
{
char *buffer;
int r;
const unsigned size = 8;
unsigned long flags;
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
r = ch341_control_in(dev, 0x95, 0x0706, 0, buffer, size);
if (r < 0)
goto out;
/* setup the private status if available */
if (r == 2) {
r = 0;
spin_lock_irqsave(&priv->lock, flags);
priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
spin_unlock_irqrestore(&priv->lock, flags);
} else
r = -EPROTO;
out: kfree(buffer);
return r;
}
/* -------------------------------------------------------------------------- */
static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
{
char *buffer;
int r;
const unsigned size = 8;
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
/* expect two bytes 0x27 0x00 */
r = ch341_control_in(dev, 0x5f, 0, 0, buffer, size);
if (r < 0)
goto out;
r = ch341_control_out(dev, 0xa1, 0, 0);
if (r < 0)
goto out;
r = ch341_set_baudrate(dev, priv);
if (r < 0)
goto out;
/* expect two bytes 0x56 0x00 */
r = ch341_control_in(dev, 0x95, 0x2518, 0, buffer, size);
if (r < 0)
goto out;
r = ch341_control_out(dev, 0x9a, 0x2518, 0x0050);
if (r < 0)
goto out;
/* expect 0xff 0xee */
r = ch341_get_status(dev, priv);
if (r < 0)
goto out;
r = ch341_control_out(dev, 0xa1, 0x501f, 0xd90a);
if (r < 0)
goto out;
r = ch341_set_baudrate(dev, priv);
if (r < 0)
goto out;
r = ch341_set_handshake(dev, priv->line_control);
if (r < 0)
goto out;
/* expect 0x9f 0xee */
r = ch341_get_status(dev, priv);
out: kfree(buffer);
return r;
}
static int ch341_port_probe(struct usb_serial_port *port)
{
struct ch341_private *priv;
int r;
priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
priv->baud_rate = DEFAULT_BAUD_RATE;
priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
r = ch341_configure(port->serial->dev, priv);
if (r < 0)
goto error;
usb_set_serial_port_data(port, priv);
return 0;
error: kfree(priv);
return r;
}
static int ch341_port_remove(struct usb_serial_port *port)
{
struct ch341_private *priv;
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static int ch341_carrier_raised(struct usb_serial_port *port)
{
struct ch341_private *priv = usb_get_serial_port_data(port);
if (priv->line_status & CH341_BIT_DCD)
return 1;
return 0;
}
static void ch341_dtr_rts(struct usb_serial_port *port, int on)
{
struct ch341_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
/* drop DTR and RTS */
spin_lock_irqsave(&priv->lock, flags);
if (on)
priv->line_control |= CH341_BIT_RTS | CH341_BIT_DTR;
else
priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR);
spin_unlock_irqrestore(&priv->lock, flags);
ch341_set_handshake(port->serial->dev, priv->line_control);
}
static void ch341_close(struct usb_serial_port *port)
{
usb_serial_generic_close(port);
usb_kill_urb(port->interrupt_in_urb);
}
/* open this device, set default parameters */
static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct ch341_private *priv = usb_get_serial_port_data(port);
int r;
priv->baud_rate = DEFAULT_BAUD_RATE;
r = ch341_configure(serial->dev, priv);
if (r)
goto out;
r = ch341_set_handshake(serial->dev, priv->line_control);
if (r)
goto out;
r = ch341_set_baudrate(serial->dev, priv);
if (r)
goto out;
dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__);
r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (r) {
dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
__func__, r);
ch341_close(port);
goto out;
}
r = usb_serial_generic_open(tty, port);
out: return r;
}
/* Old_termios contains the original termios settings and
* tty->termios contains the new setting to be used.
*/
static void ch341_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct ch341_private *priv = usb_get_serial_port_data(port);
unsigned baud_rate;
unsigned long flags;
baud_rate = tty_get_baud_rate(tty);
priv->baud_rate = baud_rate;
if (baud_rate) {
spin_lock_irqsave(&priv->lock, flags);
priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
spin_unlock_irqrestore(&priv->lock, flags);
ch341_set_baudrate(port->serial->dev, priv);
} else {
spin_lock_irqsave(&priv->lock, flags);
priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
spin_unlock_irqrestore(&priv->lock, flags);
}
ch341_set_handshake(port->serial->dev, priv->line_control);
/* Unimplemented:
* (cflag & CSIZE) : data bits [5, 8]
* (cflag & PARENB) : parity {NONE, EVEN, ODD}
* (cflag & CSTOPB) : stop bits [1, 2]
*/
}
static void ch341_break_ctl(struct tty_struct *tty, int break_state)
{
const uint16_t ch341_break_reg =
CH341_REG_BREAK1 | ((uint16_t) CH341_REG_BREAK2 << 8);
struct usb_serial_port *port = tty->driver_data;
int r;
uint16_t reg_contents;
uint8_t *break_reg;
break_reg = kmalloc(2, GFP_KERNEL);
if (!break_reg)
return;
r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG,
ch341_break_reg, 0, break_reg, 2);
if (r < 0) {
dev_err(&port->dev, "%s - USB control read error (%d)\n",
__func__, r);
goto out;
}
dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n",
__func__, break_reg[0], break_reg[1]);
if (break_state != 0) {
dev_dbg(&port->dev, "%s - Enter break state requested\n", __func__);
break_reg[0] &= ~CH341_NBREAK_BITS_REG1;
break_reg[1] &= ~CH341_NBREAK_BITS_REG2;
} else {
dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__);
break_reg[0] |= CH341_NBREAK_BITS_REG1;
break_reg[1] |= CH341_NBREAK_BITS_REG2;
}
dev_dbg(&port->dev, "%s - New ch341 break register contents - reg1: %x, reg2: %x\n",
__func__, break_reg[0], break_reg[1]);
reg_contents = get_unaligned_le16(break_reg);
r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG,
ch341_break_reg, reg_contents);
if (r < 0)
dev_err(&port->dev, "%s - USB control write error (%d)\n",
__func__, r);
out:
kfree(break_reg);
}
static int ch341_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct ch341_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
u8 control;
spin_lock_irqsave(&priv->lock, flags);
if (set & TIOCM_RTS)
priv->line_control |= CH341_BIT_RTS;
if (set & TIOCM_DTR)
priv->line_control |= CH341_BIT_DTR;
if (clear & TIOCM_RTS)
priv->line_control &= ~CH341_BIT_RTS;
if (clear & TIOCM_DTR)
priv->line_control &= ~CH341_BIT_DTR;
control = priv->line_control;
spin_unlock_irqrestore(&priv->lock, flags);
return ch341_set_handshake(port->serial->dev, control);
}
static void ch341_update_line_status(struct usb_serial_port *port,
unsigned char *data, size_t len)
{
struct ch341_private *priv = usb_get_serial_port_data(port);
struct tty_struct *tty;
unsigned long flags;
u8 status;
u8 delta;
if (len < 4)
return;
status = ~data[2] & CH341_BITS_MODEM_STAT;
spin_lock_irqsave(&priv->lock, flags);
delta = status ^ priv->line_status;
priv->line_status = status;
spin_unlock_irqrestore(&priv->lock, flags);
if (data[1] & CH341_MULT_STAT)
dev_dbg(&port->dev, "%s - multiple status change\n", __func__);
if (!delta)
return;
if (delta & CH341_BIT_CTS)
port->icount.cts++;
if (delta & CH341_BIT_DSR)
port->icount.dsr++;
if (delta & CH341_BIT_RI)
port->icount.rng++;
if (delta & CH341_BIT_DCD) {
port->icount.dcd++;
tty = tty_port_tty_get(&port->port);
if (tty) {
usb_serial_handle_dcd_change(port, tty,
status & CH341_BIT_DCD);
tty_kref_put(tty);
}
}
wake_up_interruptible(&port->port.delta_msr_wait);
}
static void ch341_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
unsigned int len = urb->actual_length;
int status;
switch (urb->status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n",
__func__, urb->status);
return;
default:
dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n",
__func__, urb->status);
goto exit;
}
usb_serial_debug_data(&port->dev, __func__, len, data);
ch341_update_line_status(port, data, len);
exit:
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status) {
dev_err(&urb->dev->dev, "%s - usb_submit_urb failed: %d\n",
__func__, status);
}
}
static int ch341_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct ch341_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
u8 mcr;
u8 status;
unsigned int result;
spin_lock_irqsave(&priv->lock, flags);
mcr = priv->line_control;
status = priv->line_status;
spin_unlock_irqrestore(&priv->lock, flags);
result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0)
| ((mcr & CH341_BIT_RTS) ? TIOCM_RTS : 0)
| ((status & CH341_BIT_CTS) ? TIOCM_CTS : 0)
| ((status & CH341_BIT_DSR) ? TIOCM_DSR : 0)
| ((status & CH341_BIT_RI) ? TIOCM_RI : 0)
| ((status & CH341_BIT_DCD) ? TIOCM_CD : 0);
dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
return result;
}
static int ch341_reset_resume(struct usb_serial *serial)
{
struct ch341_private *priv;
priv = usb_get_serial_port_data(serial->port[0]);
/* reconfigure ch341 serial port after bus-reset */
ch341_configure(serial->dev, priv);
return 0;
}
static struct usb_serial_driver ch341_device = {
.driver = {
.owner = THIS_MODULE,
.name = "ch341-uart",
},
.id_table = id_table,
.num_ports = 1,
.open = ch341_open,
.dtr_rts = ch341_dtr_rts,
.carrier_raised = ch341_carrier_raised,
.close = ch341_close,
.set_termios = ch341_set_termios,
.break_ctl = ch341_break_ctl,
.tiocmget = ch341_tiocmget,
.tiocmset = ch341_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.read_int_callback = ch341_read_int_callback,
.port_probe = ch341_port_probe,
.port_remove = ch341_port_remove,
.reset_resume = ch341_reset_resume,
};
static struct usb_serial_driver * const serial_drivers[] = {
&ch341_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,305 @@
/*
* USB Serial Console driver
*
* Copyright (C) 2001 - 2002 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* Thanks to Randy Dunlap for the original version of this code.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/console.h>
#include <linux/serial.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
struct usbcons_info {
int magic;
int break_flag;
struct usb_serial_port *port;
};
static struct usbcons_info usbcons_info;
static struct console usbcons;
/*
* ------------------------------------------------------------
* USB Serial console driver
*
* Much of the code here is copied from drivers/char/serial.c
* and implements a phony serial console in the same way that
* serial.c does so that in case some software queries it,
* it will get the same results.
*
* Things that are different from the way the serial port code
* does things, is that we call the lower level usb-serial
* driver code to initialize the device, and we set the initial
* console speeds based on the command line arguments.
* ------------------------------------------------------------
*/
static const struct tty_operations usb_console_fake_tty_ops = {
};
/*
* The parsing of the command line works exactly like the
* serial.c code, except that the specifier is "ttyUSB" instead
* of "ttyS".
*/
static int usb_console_setup(struct console *co, char *options)
{
struct usbcons_info *info = &usbcons_info;
int baud = 9600;
int bits = 8;
int parity = 'n';
int doflow = 0;
int cflag = CREAD | HUPCL | CLOCAL;
char *s;
struct usb_serial *serial;
struct usb_serial_port *port;
int retval;
struct tty_struct *tty = NULL;
struct ktermios dummy;
if (options) {
baud = simple_strtoul(options, NULL, 10);
s = options;
while (*s >= '0' && *s <= '9')
s++;
if (*s)
parity = *s++;
if (*s)
bits = *s++ - '0';
if (*s)
doflow = (*s++ == 'r');
}
/* Sane default */
if (baud == 0)
baud = 9600;
switch (bits) {
case 7:
cflag |= CS7;
break;
default:
case 8:
cflag |= CS8;
break;
}
switch (parity) {
case 'o': case 'O':
cflag |= PARODD;
break;
case 'e': case 'E':
cflag |= PARENB;
break;
}
co->cflag = cflag;
/*
* no need to check the index here: if the index is wrong, console
* code won't call us
*/
port = usb_serial_port_get_by_minor(co->index);
if (port == NULL) {
/* no device is connected yet, sorry :( */
pr_err("No USB device connected to ttyUSB%i\n", co->index);
return -ENODEV;
}
serial = port->serial;
retval = usb_autopm_get_interface(serial->interface);
if (retval)
goto error_get_interface;
tty_port_tty_set(&port->port, NULL);
info->port = port;
++port->port.count;
if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) {
if (serial->type->set_termios) {
/*
* allocate a fake tty so the driver can initialize
* the termios structure, then later call set_termios to
* configure according to command line arguments
*/
tty = kzalloc(sizeof(*tty), GFP_KERNEL);
if (!tty) {
retval = -ENOMEM;
goto reset_open_count;
}
kref_init(&tty->kref);
tty->driver = usb_serial_tty_driver;
tty->index = co->index;
init_ldsem(&tty->ldisc_sem);
INIT_LIST_HEAD(&tty->tty_files);
kref_get(&tty->driver->kref);
tty->ops = &usb_console_fake_tty_ops;
if (tty_init_termios(tty)) {
retval = -ENOMEM;
goto put_tty;
}
tty_port_tty_set(&port->port, tty);
}
/* only call the device specific open if this
* is the first time the port is opened */
retval = serial->type->open(NULL, port);
if (retval) {
dev_err(&port->dev, "could not open USB console port\n");
goto fail;
}
if (serial->type->set_termios) {
tty->termios.c_cflag = cflag;
tty_termios_encode_baud_rate(&tty->termios, baud, baud);
memset(&dummy, 0, sizeof(struct ktermios));
serial->type->set_termios(tty, port, &dummy);
tty_port_tty_set(&port->port, NULL);
tty_kref_put(tty);
}
set_bit(ASYNCB_INITIALIZED, &port->port.flags);
}
/* Now that any required fake tty operations are completed restore
* the tty port count */
--port->port.count;
/* The console is special in terms of closing the device so
* indicate this port is now acting as a system console. */
port->port.console = 1;
mutex_unlock(&serial->disc_mutex);
return retval;
fail:
tty_port_tty_set(&port->port, NULL);
put_tty:
tty_kref_put(tty);
reset_open_count:
port->port.count = 0;
usb_autopm_put_interface(serial->interface);
error_get_interface:
usb_serial_put(serial);
mutex_unlock(&serial->disc_mutex);
return retval;
}
static void usb_console_write(struct console *co,
const char *buf, unsigned count)
{
static struct usbcons_info *info = &usbcons_info;
struct usb_serial_port *port = info->port;
struct usb_serial *serial;
int retval = -ENODEV;
if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED)
return;
serial = port->serial;
if (count == 0)
return;
dev_dbg(&port->dev, "%s - %d byte(s)\n", __func__, count);
if (!port->port.console) {
dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
}
while (count) {
unsigned int i;
unsigned int lf;
/* search for LF so we can insert CR if necessary */
for (i = 0, lf = 0 ; i < count ; i++) {
if (*(buf + i) == 10) {
lf = 1;
i++;
break;
}
}
/* pass on to the driver specific version of this function if
it is available */
retval = serial->type->write(NULL, port, buf, i);
dev_dbg(&port->dev, "%s - write: %d\n", __func__, retval);
if (lf) {
/* append CR after LF */
unsigned char cr = 13;
retval = serial->type->write(NULL, port, &cr, 1);
dev_dbg(&port->dev, "%s - write cr: %d\n",
__func__, retval);
}
buf += i;
count -= i;
}
}
static struct tty_driver *usb_console_device(struct console *co, int *index)
{
struct tty_driver **p = (struct tty_driver **)co->data;
if (!*p)
return NULL;
*index = co->index;
return *p;
}
static struct console usbcons = {
.name = "ttyUSB",
.write = usb_console_write,
.device = usb_console_device,
.setup = usb_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &usb_serial_tty_driver,
};
void usb_serial_console_disconnect(struct usb_serial *serial)
{
if (serial && serial->port && serial->port[0]
&& serial->port[0] == usbcons_info.port) {
usb_serial_console_exit();
usb_serial_put(serial);
}
}
void usb_serial_console_init(int minor)
{
if (minor == 0) {
/*
* Call register_console() if this is the first device plugged
* in. If we call it earlier, then the callback to
* console_setup() will fail, as there is not a device seen by
* the USB subsystem yet.
*/
/*
* Register console.
* NOTES:
* console_setup() is called (back) immediately (from
* register_console). console_write() is called immediately
* from register_console iff CON_PRINTBUFFER is set in flags.
*/
pr_debug("registering the USB serial console.\n");
register_console(&usbcons);
}
}
void usb_serial_console_exit(void)
{
if (usbcons_info.port) {
unregister_console(&usbcons);
usbcons_info.port->port.console = 0;
usbcons_info.port = NULL;
}
}

891
drivers/usb/serial/cp210x.c Normal file
View file

@ -0,0 +1,891 @@
/*
* Silicon Laboratories CP210x USB to RS232 serial adaptor driver
*
* Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* Support to set flow control line levels using TIOCMGET and TIOCMSET
* thanks to Karl Hiramoto karl@hiramoto.org. RTSCTS hardware flow
* control thanks to Munir Nassar nassarmu@real-time.com
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/usb.h>
#include <linux/uaccess.h>
#include <linux/usb/serial.h>
#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"
/*
* Function Prototypes
*/
static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *);
static void cp210x_close(struct usb_serial_port *);
static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *);
static void cp210x_get_termios_port(struct usb_serial_port *port,
unsigned int *cflagp, unsigned int *baudp);
static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *,
struct ktermios *);
static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *,
struct ktermios*);
static int cp210x_tiocmget(struct tty_struct *);
static int cp210x_tiocmset(struct tty_struct *, unsigned int, unsigned int);
static int cp210x_tiocmset_port(struct usb_serial_port *port,
unsigned int, unsigned int);
static void cp210x_break_ctl(struct tty_struct *, int);
static int cp210x_startup(struct usb_serial *);
static void cp210x_release(struct usb_serial *);
static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x045B, 0x0053) }, /* Renesas RX610 RX-Stick */
{ USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */
{ USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
{ USB_DEVICE(0x0489, 0xE003) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
{ USB_DEVICE(0x0745, 0x1000) }, /* CipherLab USB CCD Barcode Scanner 1000 */
{ USB_DEVICE(0x0846, 0x1100) }, /* NetGear Managed Switch M4100 series, M5300 series, M7100 series */
{ USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */
{ USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */
{ USB_DEVICE(0x0908, 0x01FF) }, /* Siemens RUGGEDCOM USB Serial Console */
{ USB_DEVICE(0x0BED, 0x1100) }, /* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */
{ USB_DEVICE(0x0BED, 0x1101) }, /* MEI series 2000 Combo Acceptor */
{ USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */
{ USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */
{ USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */
{ USB_DEVICE(0x0FDE, 0xCA05) }, /* OWL Wireless Electricity Monitor CM-160 */
{ USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */
{ USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
{ USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
{ USB_DEVICE(0x10C4, 0x0F91) }, /* Vstabi */
{ USB_DEVICE(0x10C4, 0x1101) }, /* Arkham Technology DS101 Bus Monitor */
{ USB_DEVICE(0x10C4, 0x1601) }, /* Arkham Technology DS101 Adapter */
{ USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */
{ USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
{ USB_DEVICE(0x10C4, 0x8044) }, /* Cygnal Debug Adapter */
{ USB_DEVICE(0x10C4, 0x804E) }, /* Software Bisque Paramount ME build-in converter */
{ USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */
{ USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */
{ USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */
{ USB_DEVICE(0x10C4, 0x806F) }, /* IMS USB to RS422 Converter Cable */
{ USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
{ USB_DEVICE(0x10C4, 0x80C4) }, /* Cygnal Integrated Products, Inc., Optris infrared thermometer */
{ USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
{ USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */
{ USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
{ USB_DEVICE(0x10C4, 0x8115) }, /* Arygon NFC/Mifare Reader */
{ USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
{ USB_DEVICE(0x10C4, 0x813F) }, /* Tams Master Easy Control */
{ USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */
{ USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */
{ USB_DEVICE(0x2405, 0x0003) }, /* West Mountain Radio RIGblaster Advantage */
{ USB_DEVICE(0x10C4, 0x8156) }, /* B&G H3000 link cable */
{ USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */
{ USB_DEVICE(0x10C4, 0x815F) }, /* Timewave HamLinkUSB */
{ USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */
{ USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */
{ USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */
{ USB_DEVICE(0x10C4, 0x81A9) }, /* Multiplex RC Interface */
{ USB_DEVICE(0x10C4, 0x81AC) }, /* MSD Dash Hawk */
{ USB_DEVICE(0x10C4, 0x81AD) }, /* INSYS USB Modem */
{ USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */
{ USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */
{ USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */
{ USB_DEVICE(0x10C4, 0x81E8) }, /* Zephyr Bioharness */
{ USB_DEVICE(0x10C4, 0x81F2) }, /* C1007 HF band RFID controller */
{ USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */
{ USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */
{ USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */
{ USB_DEVICE(0x10C4, 0x8281) }, /* Nanotec Plug & Drive */
{ USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */
{ USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */
{ USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */
{ USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */
{ USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */
{ USB_DEVICE(0x10C4, 0x83D8) }, /* DekTec DTA Plus VHF/UHF Booster/Attenuator */
{ USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */
{ USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */
{ USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */
{ USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */
{ USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */
{ USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
{ USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */
{ USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */
{ USB_DEVICE(0x10C4, 0x8856) }, /* CEL EM357 ZigBee USB Stick - LR */
{ USB_DEVICE(0x10C4, 0x8857) }, /* CEL EM357 ZigBee USB Stick */
{ USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */
{ USB_DEVICE(0x10C4, 0x8977) }, /* CEL MeshWorks DevKit Device */
{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA80) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */
{ USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */
{ USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */
{ USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */
{ USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */
{ USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */
{ USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */
{ USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */
{ USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */
{ USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */
{ USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */
{ USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */
{ USB_DEVICE(0x166A, 0x0304) }, /* Clipsal 5000CT2 C-Bus Black and White Touchscreen */
{ USB_DEVICE(0x166A, 0x0305) }, /* Clipsal C-5000CT2 C-Bus Spectrum Colour Touchscreen */
{ USB_DEVICE(0x166A, 0x0401) }, /* Clipsal L51xx C-Bus Architectural Dimmer */
{ USB_DEVICE(0x166A, 0x0101) }, /* Clipsal 5560884 C-Bus Multi-room Audio Matrix Switcher */
{ USB_DEVICE(0x16C0, 0x09B0) }, /* Lunatico Seletek */
{ USB_DEVICE(0x16C0, 0x09B1) }, /* Lunatico Seletek */
{ USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
{ USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */
{ USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */
{ USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */
{ USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */
{ USB_DEVICE(0x17A8, 0x0001) }, /* Kamstrup Optical Eye/3-wire */
{ USB_DEVICE(0x17A8, 0x0005) }, /* Kamstrup M-Bus Master MultiPort 250D */
{ USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */
{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
{ USB_DEVICE(0x1BA4, 0x0002) }, /* Silicon Labs 358x factory default */
{ USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */
{ USB_DEVICE(0x1D6F, 0x0010) }, /* Seluxit ApS RF Dongle */
{ USB_DEVICE(0x1E29, 0x0102) }, /* Festo CPX-USB */
{ USB_DEVICE(0x1E29, 0x0501) }, /* Festo CMSP */
{ USB_DEVICE(0x1FB9, 0x0100) }, /* Lake Shore Model 121 Current Source */
{ USB_DEVICE(0x1FB9, 0x0200) }, /* Lake Shore Model 218A Temperature Monitor */
{ USB_DEVICE(0x1FB9, 0x0201) }, /* Lake Shore Model 219 Temperature Monitor */
{ USB_DEVICE(0x1FB9, 0x0202) }, /* Lake Shore Model 233 Temperature Transmitter */
{ USB_DEVICE(0x1FB9, 0x0203) }, /* Lake Shore Model 235 Temperature Transmitter */
{ USB_DEVICE(0x1FB9, 0x0300) }, /* Lake Shore Model 335 Temperature Controller */
{ USB_DEVICE(0x1FB9, 0x0301) }, /* Lake Shore Model 336 Temperature Controller */
{ USB_DEVICE(0x1FB9, 0x0302) }, /* Lake Shore Model 350 Temperature Controller */
{ USB_DEVICE(0x1FB9, 0x0303) }, /* Lake Shore Model 371 AC Bridge */
{ USB_DEVICE(0x1FB9, 0x0400) }, /* Lake Shore Model 411 Handheld Gaussmeter */
{ USB_DEVICE(0x1FB9, 0x0401) }, /* Lake Shore Model 425 Gaussmeter */
{ USB_DEVICE(0x1FB9, 0x0402) }, /* Lake Shore Model 455A Gaussmeter */
{ USB_DEVICE(0x1FB9, 0x0403) }, /* Lake Shore Model 475A Gaussmeter */
{ USB_DEVICE(0x1FB9, 0x0404) }, /* Lake Shore Model 465 Three Axis Gaussmeter */
{ USB_DEVICE(0x1FB9, 0x0600) }, /* Lake Shore Model 625A Superconducting MPS */
{ USB_DEVICE(0x1FB9, 0x0601) }, /* Lake Shore Model 642A Magnet Power Supply */
{ USB_DEVICE(0x1FB9, 0x0602) }, /* Lake Shore Model 648 Magnet Power Supply */
{ USB_DEVICE(0x1FB9, 0x0700) }, /* Lake Shore Model 737 VSM Controller */
{ USB_DEVICE(0x1FB9, 0x0701) }, /* Lake Shore Model 776 Hall Matrix */
{ USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */
{ USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */
{ USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */
{ USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
struct cp210x_serial_private {
__u8 bInterfaceNumber;
};
static struct usb_serial_driver cp210x_device = {
.driver = {
.owner = THIS_MODULE,
.name = "cp210x",
},
.id_table = id_table,
.num_ports = 1,
.bulk_in_size = 256,
.bulk_out_size = 256,
.open = cp210x_open,
.close = cp210x_close,
.break_ctl = cp210x_break_ctl,
.set_termios = cp210x_set_termios,
.tiocmget = cp210x_tiocmget,
.tiocmset = cp210x_tiocmset,
.attach = cp210x_startup,
.release = cp210x_release,
.dtr_rts = cp210x_dtr_rts
};
static struct usb_serial_driver * const serial_drivers[] = {
&cp210x_device, NULL
};
/* Config request types */
#define REQTYPE_HOST_TO_INTERFACE 0x41
#define REQTYPE_INTERFACE_TO_HOST 0xc1
#define REQTYPE_HOST_TO_DEVICE 0x40
#define REQTYPE_DEVICE_TO_HOST 0xc0
/* Config request codes */
#define CP210X_IFC_ENABLE 0x00
#define CP210X_SET_BAUDDIV 0x01
#define CP210X_GET_BAUDDIV 0x02
#define CP210X_SET_LINE_CTL 0x03
#define CP210X_GET_LINE_CTL 0x04
#define CP210X_SET_BREAK 0x05
#define CP210X_IMM_CHAR 0x06
#define CP210X_SET_MHS 0x07
#define CP210X_GET_MDMSTS 0x08
#define CP210X_SET_XON 0x09
#define CP210X_SET_XOFF 0x0A
#define CP210X_SET_EVENTMASK 0x0B
#define CP210X_GET_EVENTMASK 0x0C
#define CP210X_SET_CHAR 0x0D
#define CP210X_GET_CHARS 0x0E
#define CP210X_GET_PROPS 0x0F
#define CP210X_GET_COMM_STATUS 0x10
#define CP210X_RESET 0x11
#define CP210X_PURGE 0x12
#define CP210X_SET_FLOW 0x13
#define CP210X_GET_FLOW 0x14
#define CP210X_EMBED_EVENTS 0x15
#define CP210X_GET_EVENTSTATE 0x16
#define CP210X_SET_CHARS 0x19
#define CP210X_GET_BAUDRATE 0x1D
#define CP210X_SET_BAUDRATE 0x1E
/* CP210X_IFC_ENABLE */
#define UART_ENABLE 0x0001
#define UART_DISABLE 0x0000
/* CP210X_(SET|GET)_BAUDDIV */
#define BAUD_RATE_GEN_FREQ 0x384000
/* CP210X_(SET|GET)_LINE_CTL */
#define BITS_DATA_MASK 0X0f00
#define BITS_DATA_5 0X0500
#define BITS_DATA_6 0X0600
#define BITS_DATA_7 0X0700
#define BITS_DATA_8 0X0800
#define BITS_DATA_9 0X0900
#define BITS_PARITY_MASK 0x00f0
#define BITS_PARITY_NONE 0x0000
#define BITS_PARITY_ODD 0x0010
#define BITS_PARITY_EVEN 0x0020
#define BITS_PARITY_MARK 0x0030
#define BITS_PARITY_SPACE 0x0040
#define BITS_STOP_MASK 0x000f
#define BITS_STOP_1 0x0000
#define BITS_STOP_1_5 0x0001
#define BITS_STOP_2 0x0002
/* CP210X_SET_BREAK */
#define BREAK_ON 0x0001
#define BREAK_OFF 0x0000
/* CP210X_(SET_MHS|GET_MDMSTS) */
#define CONTROL_DTR 0x0001
#define CONTROL_RTS 0x0002
#define CONTROL_CTS 0x0010
#define CONTROL_DSR 0x0020
#define CONTROL_RING 0x0040
#define CONTROL_DCD 0x0080
#define CONTROL_WRITE_DTR 0x0100
#define CONTROL_WRITE_RTS 0x0200
/*
* cp210x_get_config
* Reads from the CP210x configuration registers
* 'size' is specified in bytes.
* 'data' is a pointer to a pre-allocated array of integers large
* enough to hold 'size' bytes (with 4 bytes to each integer)
*/
static int cp210x_get_config(struct usb_serial_port *port, u8 request,
unsigned int *data, int size)
{
struct usb_serial *serial = port->serial;
struct cp210x_serial_private *spriv = usb_get_serial_data(serial);
__le32 *buf;
int result, i, length;
/* Number of integers required to contain the array */
length = (((size - 1) | 3) + 1) / 4;
buf = kcalloc(length, sizeof(__le32), GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* Issue the request, attempting to read 'size' bytes */
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
request, REQTYPE_INTERFACE_TO_HOST, 0x0000,
spriv->bInterfaceNumber, buf, size,
USB_CTRL_GET_TIMEOUT);
/* Convert data into an array of integers */
for (i = 0; i < length; i++)
data[i] = le32_to_cpu(buf[i]);
kfree(buf);
if (result != size) {
dev_dbg(&port->dev, "%s - Unable to send config request, request=0x%x size=%d result=%d\n",
__func__, request, size, result);
if (result > 0)
result = -EPROTO;
return result;
}
return 0;
}
/*
* cp210x_set_config
* Writes to the CP210x configuration registers
* Values less than 16 bits wide are sent directly
* 'size' is specified in bytes.
*/
static int cp210x_set_config(struct usb_serial_port *port, u8 request,
unsigned int *data, int size)
{
struct usb_serial *serial = port->serial;
struct cp210x_serial_private *spriv = usb_get_serial_data(serial);
__le32 *buf;
int result, i, length;
/* Number of integers required to contain the array */
length = (((size - 1) | 3) + 1) / 4;
buf = kmalloc(length * sizeof(__le32), GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* Array of integers into bytes */
for (i = 0; i < length; i++)
buf[i] = cpu_to_le32(data[i]);
if (size > 2) {
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
request, REQTYPE_HOST_TO_INTERFACE, 0x0000,
spriv->bInterfaceNumber, buf, size,
USB_CTRL_SET_TIMEOUT);
} else {
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
request, REQTYPE_HOST_TO_INTERFACE, data[0],
spriv->bInterfaceNumber, NULL, 0,
USB_CTRL_SET_TIMEOUT);
}
kfree(buf);
if ((size > 2 && result != size) || result < 0) {
dev_dbg(&port->dev, "%s - Unable to send request, request=0x%x size=%d result=%d\n",
__func__, request, size, result);
if (result > 0)
result = -EPROTO;
return result;
}
return 0;
}
/*
* cp210x_set_config_single
* Convenience function for calling cp210x_set_config on single data values
* without requiring an integer pointer
*/
static inline int cp210x_set_config_single(struct usb_serial_port *port,
u8 request, unsigned int data)
{
return cp210x_set_config(port, request, &data, 2);
}
/*
* cp210x_quantise_baudrate
* Quantises the baud rate as per AN205 Table 1
*/
static unsigned int cp210x_quantise_baudrate(unsigned int baud)
{
if (baud <= 300)
baud = 300;
else if (baud <= 600) baud = 600;
else if (baud <= 1200) baud = 1200;
else if (baud <= 1800) baud = 1800;
else if (baud <= 2400) baud = 2400;
else if (baud <= 4000) baud = 4000;
else if (baud <= 4803) baud = 4800;
else if (baud <= 7207) baud = 7200;
else if (baud <= 9612) baud = 9600;
else if (baud <= 14428) baud = 14400;
else if (baud <= 16062) baud = 16000;
else if (baud <= 19250) baud = 19200;
else if (baud <= 28912) baud = 28800;
else if (baud <= 38601) baud = 38400;
else if (baud <= 51558) baud = 51200;
else if (baud <= 56280) baud = 56000;
else if (baud <= 58053) baud = 57600;
else if (baud <= 64111) baud = 64000;
else if (baud <= 77608) baud = 76800;
else if (baud <= 117028) baud = 115200;
else if (baud <= 129347) baud = 128000;
else if (baud <= 156868) baud = 153600;
else if (baud <= 237832) baud = 230400;
else if (baud <= 254234) baud = 250000;
else if (baud <= 273066) baud = 256000;
else if (baud <= 491520) baud = 460800;
else if (baud <= 567138) baud = 500000;
else if (baud <= 670254) baud = 576000;
else if (baud < 1000000)
baud = 921600;
else if (baud > 2000000)
baud = 2000000;
return baud;
}
static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
{
int result;
result = cp210x_set_config_single(port, CP210X_IFC_ENABLE,
UART_ENABLE);
if (result) {
dev_err(&port->dev, "%s - Unable to enable UART\n", __func__);
return result;
}
/* Configure the termios structure */
cp210x_get_termios(tty, port);
/* The baud rate must be initialised on cp2104 */
if (tty)
cp210x_change_speed(tty, port, NULL);
return usb_serial_generic_open(tty, port);
}
static void cp210x_close(struct usb_serial_port *port)
{
usb_serial_generic_close(port);
cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE);
}
/*
* cp210x_get_termios
* Reads the baud rate, data bits, parity, stop bits and flow control mode
* from the device, corrects any unsupported values, and configures the
* termios structure to reflect the state of the device
*/
static void cp210x_get_termios(struct tty_struct *tty,
struct usb_serial_port *port)
{
unsigned int baud;
if (tty) {
cp210x_get_termios_port(tty->driver_data,
&tty->termios.c_cflag, &baud);
tty_encode_baud_rate(tty, baud, baud);
} else {
unsigned int cflag;
cflag = 0;
cp210x_get_termios_port(port, &cflag, &baud);
}
}
/*
* cp210x_get_termios_port
* This is the heart of cp210x_get_termios which always uses a &usb_serial_port.
*/
static void cp210x_get_termios_port(struct usb_serial_port *port,
unsigned int *cflagp, unsigned int *baudp)
{
struct device *dev = &port->dev;
unsigned int cflag, modem_ctl[4];
unsigned int baud;
unsigned int bits;
cp210x_get_config(port, CP210X_GET_BAUDRATE, &baud, 4);
dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud);
*baudp = baud;
cflag = *cflagp;
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
cflag &= ~CSIZE;
switch (bits & BITS_DATA_MASK) {
case BITS_DATA_5:
dev_dbg(dev, "%s - data bits = 5\n", __func__);
cflag |= CS5;
break;
case BITS_DATA_6:
dev_dbg(dev, "%s - data bits = 6\n", __func__);
cflag |= CS6;
break;
case BITS_DATA_7:
dev_dbg(dev, "%s - data bits = 7\n", __func__);
cflag |= CS7;
break;
case BITS_DATA_8:
dev_dbg(dev, "%s - data bits = 8\n", __func__);
cflag |= CS8;
break;
case BITS_DATA_9:
dev_dbg(dev, "%s - data bits = 9 (not supported, using 8 data bits)\n", __func__);
cflag |= CS8;
bits &= ~BITS_DATA_MASK;
bits |= BITS_DATA_8;
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
default:
dev_dbg(dev, "%s - Unknown number of data bits, using 8\n", __func__);
cflag |= CS8;
bits &= ~BITS_DATA_MASK;
bits |= BITS_DATA_8;
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
}
switch (bits & BITS_PARITY_MASK) {
case BITS_PARITY_NONE:
dev_dbg(dev, "%s - parity = NONE\n", __func__);
cflag &= ~PARENB;
break;
case BITS_PARITY_ODD:
dev_dbg(dev, "%s - parity = ODD\n", __func__);
cflag |= (PARENB|PARODD);
break;
case BITS_PARITY_EVEN:
dev_dbg(dev, "%s - parity = EVEN\n", __func__);
cflag &= ~PARODD;
cflag |= PARENB;
break;
case BITS_PARITY_MARK:
dev_dbg(dev, "%s - parity = MARK\n", __func__);
cflag |= (PARENB|PARODD|CMSPAR);
break;
case BITS_PARITY_SPACE:
dev_dbg(dev, "%s - parity = SPACE\n", __func__);
cflag &= ~PARODD;
cflag |= (PARENB|CMSPAR);
break;
default:
dev_dbg(dev, "%s - Unknown parity mode, disabling parity\n", __func__);
cflag &= ~PARENB;
bits &= ~BITS_PARITY_MASK;
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
}
cflag &= ~CSTOPB;
switch (bits & BITS_STOP_MASK) {
case BITS_STOP_1:
dev_dbg(dev, "%s - stop bits = 1\n", __func__);
break;
case BITS_STOP_1_5:
dev_dbg(dev, "%s - stop bits = 1.5 (not supported, using 1 stop bit)\n", __func__);
bits &= ~BITS_STOP_MASK;
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
case BITS_STOP_2:
dev_dbg(dev, "%s - stop bits = 2\n", __func__);
cflag |= CSTOPB;
break;
default:
dev_dbg(dev, "%s - Unknown number of stop bits, using 1 stop bit\n", __func__);
bits &= ~BITS_STOP_MASK;
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
}
cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
if (modem_ctl[0] & 0x0008) {
dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__);
cflag |= CRTSCTS;
} else {
dev_dbg(dev, "%s - flow control = NONE\n", __func__);
cflag &= ~CRTSCTS;
}
*cflagp = cflag;
}
/*
* CP2101 supports the following baud rates:
*
* 300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 28800,
* 38400, 56000, 57600, 115200, 128000, 230400, 460800, 921600
*
* CP2102 and CP2103 support the following additional rates:
*
* 4000, 16000, 51200, 64000, 76800, 153600, 250000, 256000, 500000,
* 576000
*
* The device will map a requested rate to a supported one, but the result
* of requests for rates greater than 1053257 is undefined (see AN205).
*
* CP2104, CP2105 and CP2110 support most rates up to 2M, 921k and 1M baud,
* respectively, with an error less than 1%. The actual rates are determined
* by
*
* div = round(freq / (2 x prescale x request))
* actual = freq / (2 x prescale x div)
*
* For CP2104 and CP2105 freq is 48Mhz and prescale is 4 for request <= 365bps
* or 1 otherwise.
* For CP2110 freq is 24Mhz and prescale is 4 for request <= 300bps or 1
* otherwise.
*/
static void cp210x_change_speed(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
u32 baud;
baud = tty->termios.c_ospeed;
/* This maps the requested rate to a rate valid on cp2102 or cp2103,
* or to an arbitrary rate in [1M,2M].
*
* NOTE: B0 is not implemented.
*/
baud = cp210x_quantise_baudrate(baud);
dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud);
if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud,
sizeof(baud))) {
dev_warn(&port->dev, "failed to set baud rate to %u\n", baud);
if (old_termios)
baud = old_termios->c_ospeed;
else
baud = 9600;
}
tty_encode_baud_rate(tty, baud, baud);
}
static void cp210x_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct device *dev = &port->dev;
unsigned int cflag, old_cflag;
unsigned int bits;
unsigned int modem_ctl[4];
cflag = tty->termios.c_cflag;
old_cflag = old_termios->c_cflag;
if (tty->termios.c_ospeed != old_termios->c_ospeed)
cp210x_change_speed(tty, port, old_termios);
/* If the number of data bits is to be updated */
if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
bits &= ~BITS_DATA_MASK;
switch (cflag & CSIZE) {
case CS5:
bits |= BITS_DATA_5;
dev_dbg(dev, "%s - data bits = 5\n", __func__);
break;
case CS6:
bits |= BITS_DATA_6;
dev_dbg(dev, "%s - data bits = 6\n", __func__);
break;
case CS7:
bits |= BITS_DATA_7;
dev_dbg(dev, "%s - data bits = 7\n", __func__);
break;
case CS8:
bits |= BITS_DATA_8;
dev_dbg(dev, "%s - data bits = 8\n", __func__);
break;
/*case CS9:
bits |= BITS_DATA_9;
dev_dbg(dev, "%s - data bits = 9\n", __func__);
break;*/
default:
dev_dbg(dev, "cp210x driver does not support the number of bits requested, using 8 bit mode\n");
bits |= BITS_DATA_8;
break;
}
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
dev_dbg(dev, "Number of data bits requested not supported by device\n");
}
if ((cflag & (PARENB|PARODD|CMSPAR)) !=
(old_cflag & (PARENB|PARODD|CMSPAR))) {
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
bits &= ~BITS_PARITY_MASK;
if (cflag & PARENB) {
if (cflag & CMSPAR) {
if (cflag & PARODD) {
bits |= BITS_PARITY_MARK;
dev_dbg(dev, "%s - parity = MARK\n", __func__);
} else {
bits |= BITS_PARITY_SPACE;
dev_dbg(dev, "%s - parity = SPACE\n", __func__);
}
} else {
if (cflag & PARODD) {
bits |= BITS_PARITY_ODD;
dev_dbg(dev, "%s - parity = ODD\n", __func__);
} else {
bits |= BITS_PARITY_EVEN;
dev_dbg(dev, "%s - parity = EVEN\n", __func__);
}
}
}
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
dev_dbg(dev, "Parity mode not supported by device\n");
}
if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
bits &= ~BITS_STOP_MASK;
if (cflag & CSTOPB) {
bits |= BITS_STOP_2;
dev_dbg(dev, "%s - stop bits = 2\n", __func__);
} else {
bits |= BITS_STOP_1;
dev_dbg(dev, "%s - stop bits = 1\n", __func__);
}
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
dev_dbg(dev, "Number of stop bits requested not supported by device\n");
}
if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
dev_dbg(dev, "%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x\n",
__func__, modem_ctl[0], modem_ctl[1],
modem_ctl[2], modem_ctl[3]);
if (cflag & CRTSCTS) {
modem_ctl[0] &= ~0x7B;
modem_ctl[0] |= 0x09;
modem_ctl[1] = 0x80;
dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__);
} else {
modem_ctl[0] &= ~0x7B;
modem_ctl[0] |= 0x01;
modem_ctl[1] |= 0x40;
dev_dbg(dev, "%s - flow control = NONE\n", __func__);
}
dev_dbg(dev, "%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x\n",
__func__, modem_ctl[0], modem_ctl[1],
modem_ctl[2], modem_ctl[3]);
cp210x_set_config(port, CP210X_SET_FLOW, modem_ctl, 16);
}
}
static int cp210x_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
return cp210x_tiocmset_port(port, set, clear);
}
static int cp210x_tiocmset_port(struct usb_serial_port *port,
unsigned int set, unsigned int clear)
{
unsigned int control = 0;
if (set & TIOCM_RTS) {
control |= CONTROL_RTS;
control |= CONTROL_WRITE_RTS;
}
if (set & TIOCM_DTR) {
control |= CONTROL_DTR;
control |= CONTROL_WRITE_DTR;
}
if (clear & TIOCM_RTS) {
control &= ~CONTROL_RTS;
control |= CONTROL_WRITE_RTS;
}
if (clear & TIOCM_DTR) {
control &= ~CONTROL_DTR;
control |= CONTROL_WRITE_DTR;
}
dev_dbg(&port->dev, "%s - control = 0x%.4x\n", __func__, control);
return cp210x_set_config(port, CP210X_SET_MHS, &control, 2);
}
static void cp210x_dtr_rts(struct usb_serial_port *p, int on)
{
if (on)
cp210x_tiocmset_port(p, TIOCM_DTR|TIOCM_RTS, 0);
else
cp210x_tiocmset_port(p, 0, TIOCM_DTR|TIOCM_RTS);
}
static int cp210x_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
unsigned int control;
int result;
cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1);
result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0)
|((control & CONTROL_RTS) ? TIOCM_RTS : 0)
|((control & CONTROL_CTS) ? TIOCM_CTS : 0)
|((control & CONTROL_DSR) ? TIOCM_DSR : 0)
|((control & CONTROL_RING)? TIOCM_RI : 0)
|((control & CONTROL_DCD) ? TIOCM_CD : 0);
dev_dbg(&port->dev, "%s - control = 0x%.2x\n", __func__, control);
return result;
}
static void cp210x_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
unsigned int state;
if (break_state == 0)
state = BREAK_OFF;
else
state = BREAK_ON;
dev_dbg(&port->dev, "%s - turning break %s\n", __func__,
state == BREAK_OFF ? "off" : "on");
cp210x_set_config(port, CP210X_SET_BREAK, &state, 2);
}
static int cp210x_startup(struct usb_serial *serial)
{
struct usb_host_interface *cur_altsetting;
struct cp210x_serial_private *spriv;
spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
if (!spriv)
return -ENOMEM;
cur_altsetting = serial->interface->cur_altsetting;
spriv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
usb_set_serial_data(serial, spriv);
return 0;
}
static void cp210x_release(struct usb_serial *serial)
{
struct cp210x_serial_private *spriv;
spriv = usb_get_serial_data(serial);
kfree(spriv);
}
module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,421 @@
/*
* REINER SCT cyberJack pinpad/e-com USB Chipcard Reader Driver
*
* Copyright (C) 2001 REINER SCT
* Author: Matthias Bruestle
*
* Contact: support@reiner-sct.com (see MAINTAINERS)
*
* This program is largely derived from work by the linux-usb group
* and associated source files. Please see the usb/serial files for
* individual credits and copyrights.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Thanks to Greg Kroah-Hartman (greg@kroah.com) for his help and
* patience.
*
* In case of problems, please write to the contact e-mail address
* mentioned above.
*
* Please note that later models of the cyberjack reader family are
* supported by a libusb-based userspace device driver.
*
* Homepage: http://www.reiner-sct.de/support/treiber_cyberjack.php#linux
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#define CYBERJACK_LOCAL_BUF_SIZE 32
#define DRIVER_AUTHOR "Matthias Bruestle"
#define DRIVER_DESC "REINER SCT cyberJack pinpad/e-com USB Chipcard Reader Driver"
#define CYBERJACK_VENDOR_ID 0x0C4B
#define CYBERJACK_PRODUCT_ID 0x0100
/* Function prototypes */
static int cyberjack_port_probe(struct usb_serial_port *port);
static int cyberjack_port_remove(struct usb_serial_port *port);
static int cyberjack_open(struct tty_struct *tty,
struct usb_serial_port *port);
static void cyberjack_close(struct usb_serial_port *port);
static int cyberjack_write(struct tty_struct *tty,
struct usb_serial_port *port, const unsigned char *buf, int count);
static int cyberjack_write_room(struct tty_struct *tty);
static void cyberjack_read_int_callback(struct urb *urb);
static void cyberjack_read_bulk_callback(struct urb *urb);
static void cyberjack_write_bulk_callback(struct urb *urb);
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(CYBERJACK_VENDOR_ID, CYBERJACK_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver cyberjack_device = {
.driver = {
.owner = THIS_MODULE,
.name = "cyberjack",
},
.description = "Reiner SCT Cyberjack USB card reader",
.id_table = id_table,
.num_ports = 1,
.port_probe = cyberjack_port_probe,
.port_remove = cyberjack_port_remove,
.open = cyberjack_open,
.close = cyberjack_close,
.write = cyberjack_write,
.write_room = cyberjack_write_room,
.read_int_callback = cyberjack_read_int_callback,
.read_bulk_callback = cyberjack_read_bulk_callback,
.write_bulk_callback = cyberjack_write_bulk_callback,
};
static struct usb_serial_driver * const serial_drivers[] = {
&cyberjack_device, NULL
};
struct cyberjack_private {
spinlock_t lock; /* Lock for SMP */
short rdtodo; /* Bytes still to read */
unsigned char wrbuf[5*64]; /* Buffer for collecting data to write */
short wrfilled; /* Overall data size we already got */
short wrsent; /* Data already sent */
};
static int cyberjack_port_probe(struct usb_serial_port *port)
{
struct cyberjack_private *priv;
int result;
priv = kmalloc(sizeof(struct cyberjack_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
priv->rdtodo = 0;
priv->wrfilled = 0;
priv->wrsent = 0;
usb_set_serial_port_data(port, priv);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result)
dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
return 0;
}
static int cyberjack_port_remove(struct usb_serial_port *port)
{
struct cyberjack_private *priv;
usb_kill_urb(port->interrupt_in_urb);
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static int cyberjack_open(struct tty_struct *tty,
struct usb_serial_port *port)
{
struct cyberjack_private *priv;
unsigned long flags;
int result = 0;
dev_dbg(&port->dev, "%s - usb_clear_halt\n", __func__);
usb_clear_halt(port->serial->dev, port->write_urb->pipe);
priv = usb_get_serial_port_data(port);
spin_lock_irqsave(&priv->lock, flags);
priv->rdtodo = 0;
priv->wrfilled = 0;
priv->wrsent = 0;
spin_unlock_irqrestore(&priv->lock, flags);
return result;
}
static void cyberjack_close(struct usb_serial_port *port)
{
usb_kill_urb(port->write_urb);
usb_kill_urb(port->read_urb);
}
static int cyberjack_write(struct tty_struct *tty,
struct usb_serial_port *port, const unsigned char *buf, int count)
{
struct device *dev = &port->dev;
struct cyberjack_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
int result;
int wrexpected;
if (count == 0) {
dev_dbg(dev, "%s - write request of 0 bytes\n", __func__);
return 0;
}
if (!test_and_clear_bit(0, &port->write_urbs_free)) {
dev_dbg(dev, "%s - already writing\n", __func__);
return 0;
}
spin_lock_irqsave(&priv->lock, flags);
if (count+priv->wrfilled > sizeof(priv->wrbuf)) {
/* To much data for buffer. Reset buffer. */
priv->wrfilled = 0;
spin_unlock_irqrestore(&priv->lock, flags);
set_bit(0, &port->write_urbs_free);
return 0;
}
/* Copy data */
memcpy(priv->wrbuf + priv->wrfilled, buf, count);
usb_serial_debug_data(dev, __func__, count, priv->wrbuf + priv->wrfilled);
priv->wrfilled += count;
if (priv->wrfilled >= 3) {
wrexpected = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3;
dev_dbg(dev, "%s - expected data: %d\n", __func__, wrexpected);
} else
wrexpected = sizeof(priv->wrbuf);
if (priv->wrfilled >= wrexpected) {
/* We have enough data to begin transmission */
int length;
dev_dbg(dev, "%s - transmitting data (frame 1)\n", __func__);
length = (wrexpected > port->bulk_out_size) ?
port->bulk_out_size : wrexpected;
memcpy(port->write_urb->transfer_buffer, priv->wrbuf, length);
priv->wrsent = length;
/* set up our urb */
port->write_urb->transfer_buffer_length = length;
/* send the data out the bulk port */
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result) {
dev_err(&port->dev,
"%s - failed submitting write urb, error %d\n",
__func__, result);
/* Throw away data. No better idea what to do with it. */
priv->wrfilled = 0;
priv->wrsent = 0;
spin_unlock_irqrestore(&priv->lock, flags);
set_bit(0, &port->write_urbs_free);
return 0;
}
dev_dbg(dev, "%s - priv->wrsent=%d\n", __func__, priv->wrsent);
dev_dbg(dev, "%s - priv->wrfilled=%d\n", __func__, priv->wrfilled);
if (priv->wrsent >= priv->wrfilled) {
dev_dbg(dev, "%s - buffer cleaned\n", __func__);
memset(priv->wrbuf, 0, sizeof(priv->wrbuf));
priv->wrfilled = 0;
priv->wrsent = 0;
}
}
spin_unlock_irqrestore(&priv->lock, flags);
return count;
}
static int cyberjack_write_room(struct tty_struct *tty)
{
/* FIXME: .... */
return CYBERJACK_LOCAL_BUF_SIZE;
}
static void cyberjack_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct cyberjack_private *priv = usb_get_serial_port_data(port);
struct device *dev = &port->dev;
unsigned char *data = urb->transfer_buffer;
int status = urb->status;
int result;
/* the urb might have been killed. */
if (status)
return;
usb_serial_debug_data(dev, __func__, urb->actual_length, data);
/* React only to interrupts signaling a bulk_in transfer */
if (urb->actual_length == 4 && data[0] == 0x01) {
short old_rdtodo;
/* This is a announcement of coming bulk_ins. */
unsigned short size = ((unsigned short)data[3]<<8)+data[2]+3;
spin_lock(&priv->lock);
old_rdtodo = priv->rdtodo;
if (old_rdtodo > SHRT_MAX - size) {
dev_dbg(dev, "To many bulk_in urbs to do.\n");
spin_unlock(&priv->lock);
goto resubmit;
}
/* "+=" is probably more fault tolerant than "=" */
priv->rdtodo += size;
dev_dbg(dev, "%s - rdtodo: %d\n", __func__, priv->rdtodo);
spin_unlock(&priv->lock);
if (!old_rdtodo) {
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result)
dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
__func__, result);
dev_dbg(dev, "%s - usb_submit_urb(read urb)\n", __func__);
}
}
resubmit:
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
if (result)
dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
dev_dbg(dev, "%s - usb_submit_urb(int urb)\n", __func__);
}
static void cyberjack_read_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct cyberjack_private *priv = usb_get_serial_port_data(port);
struct device *dev = &port->dev;
unsigned char *data = urb->transfer_buffer;
short todo;
int result;
int status = urb->status;
usb_serial_debug_data(dev, __func__, urb->actual_length, data);
if (status) {
dev_dbg(dev, "%s - nonzero read bulk status received: %d\n",
__func__, status);
return;
}
if (urb->actual_length) {
tty_insert_flip_string(&port->port, data, urb->actual_length);
tty_flip_buffer_push(&port->port);
}
spin_lock(&priv->lock);
/* Reduce urbs to do by one. */
priv->rdtodo -= urb->actual_length;
/* Just to be sure */
if (priv->rdtodo < 0)
priv->rdtodo = 0;
todo = priv->rdtodo;
spin_unlock(&priv->lock);
dev_dbg(dev, "%s - rdtodo: %d\n", __func__, todo);
/* Continue to read if we have still urbs to do. */
if (todo /* || (urb->actual_length==port->bulk_in_endpointAddress)*/) {
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result)
dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
__func__, result);
dev_dbg(dev, "%s - usb_submit_urb(read urb)\n", __func__);
}
}
static void cyberjack_write_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct cyberjack_private *priv = usb_get_serial_port_data(port);
struct device *dev = &port->dev;
int status = urb->status;
set_bit(0, &port->write_urbs_free);
if (status) {
dev_dbg(dev, "%s - nonzero write bulk status received: %d\n",
__func__, status);
return;
}
spin_lock(&priv->lock);
/* only do something if we have more data to send */
if (priv->wrfilled) {
int length, blksize, result;
dev_dbg(dev, "%s - transmitting data (frame n)\n", __func__);
length = ((priv->wrfilled - priv->wrsent) > port->bulk_out_size) ?
port->bulk_out_size : (priv->wrfilled - priv->wrsent);
memcpy(port->write_urb->transfer_buffer,
priv->wrbuf + priv->wrsent, length);
priv->wrsent += length;
/* set up our urb */
port->write_urb->transfer_buffer_length = length;
/* send the data out the bulk port */
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result) {
dev_err(dev, "%s - failed submitting write urb, error %d\n",
__func__, result);
/* Throw away data. No better idea what to do with it. */
priv->wrfilled = 0;
priv->wrsent = 0;
goto exit;
}
dev_dbg(dev, "%s - priv->wrsent=%d\n", __func__, priv->wrsent);
dev_dbg(dev, "%s - priv->wrfilled=%d\n", __func__, priv->wrfilled);
blksize = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3;
if (priv->wrsent >= priv->wrfilled ||
priv->wrsent >= blksize) {
dev_dbg(dev, "%s - buffer cleaned\n", __func__);
memset(priv->wrbuf, 0, sizeof(priv->wrbuf));
priv->wrfilled = 0;
priv->wrsent = 0;
}
}
exit:
spin_unlock(&priv->lock);
usb_serial_port_softint(port);
}
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,78 @@
#ifndef CYPRESS_M8_H
#define CYPRESS_M8_H
/*
* definitions and function prototypes used for the cypress USB to Serial
* controller
*/
/*
* For sending our feature buffer - controlling serial communication states.
* Linux HID has no support for serial devices so we do this through the driver
*/
#define HID_REQ_GET_REPORT 0x01
#define HID_REQ_SET_REPORT 0x09
/* List other cypress USB to Serial devices here, and add them to the id_table */
/* DeLorme Earthmate USB - a GPS device */
#define VENDOR_ID_DELORME 0x1163
#define PRODUCT_ID_EARTHMATEUSB 0x0100
#define PRODUCT_ID_EARTHMATEUSB_LT20 0x0200
/* Cypress HID->COM RS232 Adapter */
#define VENDOR_ID_CYPRESS 0x04b4
#define PRODUCT_ID_CYPHIDCOM 0x5500
/* FRWD Dongle - a GPS sports watch */
#define VENDOR_ID_FRWD 0x6737
#define PRODUCT_ID_CYPHIDCOM_FRWD 0x0001
/* Powercom UPS, chip CY7C63723 */
#define VENDOR_ID_POWERCOM 0x0d9f
#define PRODUCT_ID_UPS 0x0002
/* Nokia CA-42 USB to serial cable */
#define VENDOR_ID_DAZZLE 0x07d0
#define PRODUCT_ID_CA42 0x4101
/* End of device listing */
/* Used for setting / requesting serial line settings */
#define CYPRESS_SET_CONFIG 0x01
#define CYPRESS_GET_CONFIG 0x02
/* Used for throttle control */
#define THROTTLED 0x1
#define ACTUALLY_THROTTLED 0x2
/*
* chiptypes - used in case firmware differs from the generic form ... offering
* different baud speeds/etc.
*/
#define CT_EARTHMATE 0x01
#define CT_CYPHIDCOM 0x02
#define CT_CA42V2 0x03
#define CT_GENERIC 0x0F
/* End of chiptype definitions */
/*
* RS-232 serial data communication protocol definitions.
*
* These are sent / read at byte 0 of the input/output hid reports.
* You can find these values defined in the CY4601 USB to Serial design notes.
*/
#define CONTROL_DTR 0x20 /* data terminal ready */
#define CONTROL_RTS 0x10 /* request to send */
#define CONTROL_RESET 0x08 /* sent with output report */
#define UART_MSR_MASK 0xf0
#define UART_RI 0x80 /* ring indicator */
#define UART_CD 0x40 /* carrier detect */
#define UART_DSR 0x20 /* data set ready */
#define UART_CTS 0x10 /* clear to send */
#define CYP_ERROR 0x08 /* received from input report */
/* End of RS-232 protocol definitions */
#endif /* CYPRESS_M8_H */

File diff suppressed because it is too large Load diff

129
drivers/usb/serial/empeg.c Normal file
View file

@ -0,0 +1,129 @@
/*
* USB Empeg empeg-car player driver
*
* Copyright (C) 2000, 2001
* Gary Brubaker (xavyer@ix.netcom.com)
*
* Copyright (C) 1999 - 2001
* Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, as published by
* the Free Software Foundation, version 2.
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Gary Brubaker <xavyer@ix.netcom.com>"
#define DRIVER_DESC "USB Empeg Mark I/II Driver"
#define EMPEG_VENDOR_ID 0x084f
#define EMPEG_PRODUCT_ID 0x0001
/* function prototypes for an empeg-car player */
static int empeg_startup(struct usb_serial *serial);
static void empeg_init_termios(struct tty_struct *tty);
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(EMPEG_VENDOR_ID, EMPEG_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver empeg_device = {
.driver = {
.owner = THIS_MODULE,
.name = "empeg",
},
.id_table = id_table,
.num_ports = 1,
.bulk_out_size = 256,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
.attach = empeg_startup,
.init_termios = empeg_init_termios,
};
static struct usb_serial_driver * const serial_drivers[] = {
&empeg_device, NULL
};
static int empeg_startup(struct usb_serial *serial)
{
int r;
if (serial->dev->actconfig->desc.bConfigurationValue != 1) {
dev_err(&serial->dev->dev, "active config #%d != 1 ??\n",
serial->dev->actconfig->desc.bConfigurationValue);
return -ENODEV;
}
r = usb_reset_configuration(serial->dev);
/* continue on with initialization */
return r;
}
static void empeg_init_termios(struct tty_struct *tty)
{
struct ktermios *termios = &tty->termios;
/*
* The empeg-car player wants these particular tty settings.
* You could, for example, change the baud rate, however the
* player only supports 115200 (currently), so there is really
* no point in support for changes to the tty settings.
* (at least for now)
*
* The default requirements for this device are:
*/
termios->c_iflag
&= ~(IGNBRK /* disable ignore break */
| BRKINT /* disable break causes interrupt */
| PARMRK /* disable mark parity errors */
| ISTRIP /* disable clear high bit of input characters */
| INLCR /* disable translate NL to CR */
| IGNCR /* disable ignore CR */
| ICRNL /* disable translate CR to NL */
| IXON); /* disable enable XON/XOFF flow control */
termios->c_oflag
&= ~OPOST; /* disable postprocess output characters */
termios->c_lflag
&= ~(ECHO /* disable echo input characters */
| ECHONL /* disable echo new line */
| ICANON /* disable erase, kill, werase, and rprnt special characters */
| ISIG /* disable interrupt, quit, and suspend special characters */
| IEXTEN); /* disable non-POSIX special characters */
termios->c_cflag
&= ~(CSIZE /* no size */
| PARENB /* disable parity bit */
| CBAUD); /* clear current baud rate */
termios->c_cflag
|= CS8; /* character size 8 bits */
tty_encode_baud_rate(tty, 115200, 115200);
}
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,50 @@
#! /usr/bin/perl -w
# convert an Intel HEX file into a set of C records usable by the firmware
# loading code in usb-serial.c (or others)
# accepts the .hex file(s) on stdin, a basename (to name the initialized
# array) as an argument, and prints the .h file to stdout. Typical usage:
# perl ezusb_convert.pl foo <foo.hex >fw_foo.h
my $basename = $ARGV[0];
die "no base name specified" unless $basename;
while (<STDIN>) {
# ':' <len> <addr> <type> <len-data> <crc> '\r'
# len, type, crc are 2-char hex, addr is 4-char hex. type is 00 for
# normal records, 01 for EOF
my($lenstring, $addrstring, $typestring, $reststring, $doscrap) =
/^:(\w\w)(\w\w\w\w)(\w\w)(\w+)(\r?)$/;
die "malformed line: $_" unless $reststring;
last if $typestring eq '01';
my($len) = hex($lenstring);
my($addr) = hex($addrstring);
my(@bytes) = unpack("C*", pack("H".(2*$len), $reststring));
#pop(@bytes); # last byte is a CRC
push(@records, [$addr, \@bytes]);
}
@sorted_records = sort { $a->[0] <=> $b->[0] } @records;
print <<"EOF";
/*
* ${basename}_fw.h
*
* Generated from ${basename}.s by ezusb_convert.pl
* This file is presumed to be under the same copyright as the source file
* from which it was derived.
*/
EOF
print "static const struct ezusb_hex_record ${basename}_firmware[] = {\n";
foreach $r (@sorted_records) {
printf("{ 0x%04x,\t%d,\t{", $r->[0], scalar(@{$r->[1]}));
print join(", ", map {sprintf('0x%02x', $_);} @{$r->[1]});
print "} },\n";
}
print "{ 0xffff,\t0,\t{0x00} }\n";
print "};\n";

334
drivers/usb/serial/f81232.c Normal file
View file

@ -0,0 +1,334 @@
/*
* Fintek F81232 USB to serial adaptor driver
*
* Copyright (C) 2012 Greg Kroah-Hartman (gregkh@linuxfoundation.org)
* Copyright (C) 2012 Linux Foundation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1934, 0x0706) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
#define CONTROL_DTR 0x01
#define CONTROL_RTS 0x02
#define UART_STATE 0x08
#define UART_STATE_TRANSIENT_MASK 0x74
#define UART_DCD 0x01
#define UART_DSR 0x02
#define UART_BREAK_ERROR 0x04
#define UART_RING 0x08
#define UART_FRAME_ERROR 0x10
#define UART_PARITY_ERROR 0x20
#define UART_OVERRUN_ERROR 0x40
#define UART_CTS 0x80
struct f81232_private {
spinlock_t lock;
u8 line_control;
u8 line_status;
};
static void f81232_update_line_status(struct usb_serial_port *port,
unsigned char *data,
unsigned int actual_length)
{
/*
* FIXME: Update port->icount, and call
*
* wake_up_interruptible(&port->port.delta_msr_wait);
*
* on MSR changes.
*/
}
static void f81232_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
unsigned int actual_length = urb->actual_length;
int status = urb->status;
int retval;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
__func__, status);
goto exit;
}
usb_serial_debug_data(&port->dev, __func__,
urb->actual_length, urb->transfer_buffer);
f81232_update_line_status(port, data, actual_length);
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
dev_err(&urb->dev->dev,
"%s - usb_submit_urb failed with result %d\n",
__func__, retval);
}
static void f81232_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct f81232_private *priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
char tty_flag = TTY_NORMAL;
unsigned long flags;
u8 line_status;
int i;
/* update line status */
spin_lock_irqsave(&priv->lock, flags);
line_status = priv->line_status;
priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
spin_unlock_irqrestore(&priv->lock, flags);
if (!urb->actual_length)
return;
/* break takes precedence over parity, */
/* which takes precedence over framing errors */
if (line_status & UART_BREAK_ERROR)
tty_flag = TTY_BREAK;
else if (line_status & UART_PARITY_ERROR)
tty_flag = TTY_PARITY;
else if (line_status & UART_FRAME_ERROR)
tty_flag = TTY_FRAME;
dev_dbg(&port->dev, "%s - tty_flag = %d\n", __func__, tty_flag);
/* overrun is special, not associated with a char */
if (line_status & UART_OVERRUN_ERROR)
tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
if (port->port.console && port->sysrq) {
for (i = 0; i < urb->actual_length; ++i)
if (!usb_serial_handle_sysrq_char(port, data[i]))
tty_insert_flip_char(&port->port, data[i],
tty_flag);
} else {
tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag,
urb->actual_length);
}
tty_flip_buffer_push(&port->port);
}
static int set_control_lines(struct usb_device *dev, u8 value)
{
/* FIXME - Stubbed out for now */
return 0;
}
static void f81232_break_ctl(struct tty_struct *tty, int break_state)
{
/* FIXME - Stubbed out for now */
/*
* break_state = -1 to turn on break, and 0 to turn off break
* see drivers/char/tty_io.c to see it used.
* last_set_data_urb_value NEVER has the break bit set in it.
*/
}
static void f81232_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
/* FIXME - Stubbed out for now */
/* Don't change anything if nothing has changed */
if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
return;
/* Do the real work here... */
if (old_termios)
tty_termios_copy_hw(&tty->termios, old_termios);
}
static int f81232_tiocmget(struct tty_struct *tty)
{
/* FIXME - Stubbed out for now */
return 0;
}
static int f81232_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
/* FIXME - Stubbed out for now */
return 0;
}
static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port)
{
int result;
/* Setup termios */
if (tty)
f81232_set_termios(tty, port, NULL);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result) {
dev_err(&port->dev, "%s - failed submitting interrupt urb,"
" error %d\n", __func__, result);
return result;
}
result = usb_serial_generic_open(tty, port);
if (result) {
usb_kill_urb(port->interrupt_in_urb);
return result;
}
return 0;
}
static void f81232_close(struct usb_serial_port *port)
{
usb_serial_generic_close(port);
usb_kill_urb(port->interrupt_in_urb);
}
static void f81232_dtr_rts(struct usb_serial_port *port, int on)
{
struct f81232_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
u8 control;
spin_lock_irqsave(&priv->lock, flags);
/* Change DTR and RTS */
if (on)
priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
else
priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
control = priv->line_control;
spin_unlock_irqrestore(&priv->lock, flags);
set_control_lines(port->serial->dev, control);
}
static int f81232_carrier_raised(struct usb_serial_port *port)
{
struct f81232_private *priv = usb_get_serial_port_data(port);
if (priv->line_status & UART_DCD)
return 1;
return 0;
}
static int f81232_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct serial_struct ser;
struct usb_serial_port *port = tty->driver_data;
switch (cmd) {
case TIOCGSERIAL:
memset(&ser, 0, sizeof ser);
ser.type = PORT_16654;
ser.line = port->minor;
ser.port = port->port_number;
ser.baud_base = 460800;
if (copy_to_user((void __user *)arg, &ser, sizeof ser))
return -EFAULT;
return 0;
default:
break;
}
return -ENOIOCTLCMD;
}
static int f81232_port_probe(struct usb_serial_port *port)
{
struct f81232_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
usb_set_serial_port_data(port, priv);
port->port.drain_delay = 256;
return 0;
}
static int f81232_port_remove(struct usb_serial_port *port)
{
struct f81232_private *priv;
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static struct usb_serial_driver f81232_device = {
.driver = {
.owner = THIS_MODULE,
.name = "f81232",
},
.id_table = id_table,
.num_ports = 1,
.bulk_in_size = 256,
.bulk_out_size = 256,
.open = f81232_open,
.close = f81232_close,
.dtr_rts = f81232_dtr_rts,
.carrier_raised = f81232_carrier_raised,
.ioctl = f81232_ioctl,
.break_ctl = f81232_break_ctl,
.set_termios = f81232_set_termios,
.tiocmget = f81232_tiocmget,
.tiocmset = f81232_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.process_read_urb = f81232_process_read_urb,
.read_int_callback = f81232_read_int_callback,
.port_probe = f81232_port_probe,
.port_remove = f81232_port_remove,
};
static struct usb_serial_driver * const serial_drivers[] = {
&f81232_device,
NULL,
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver");
MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,564 @@
/*
* Driver definitions for the FTDI USB Single Port Serial Converter -
* known as FTDI_SIO (Serial Input/Output application of the chipset)
*
* For USB vendor/product IDs (VID/PID), please see ftdi_sio_ids.h
*
*
* The example I have is known as the USC-1000 which is available from
* http://www.dse.co.nz - cat no XH4214 It looks similar to this:
* http://www.dansdata.com/usbser.htm but I can't be sure There are other
* USC-1000s which don't look like my device though so beware!
*
* The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side,
* USB on the other.
*
* Thanx to FTDI (http://www.ftdichip.com) for so kindly providing details
* of the protocol required to talk to the device and ongoing assistence
* during development.
*
* Bill Ryder - bryder@sgi.com formerly of Silicon Graphics, Inc.- wrote the
* FTDI_SIO implementation.
*
*/
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */
#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */
#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of
the port */
#define FTDI_SIO_GET_MODEM_STATUS 5 /* Retrieve current value of modem
status register */
#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */
#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */
#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */
#define FTDI_SIO_GET_LATENCY_TIMER 10 /* Get the latency timer */
/* Interface indices for FT2232, FT2232H and FT4232H devices */
#define INTERFACE_A 1
#define INTERFACE_B 2
#define INTERFACE_C 3
#define INTERFACE_D 4
/*
* BmRequestType: 1100 0000b
* bRequest: FTDI_E2_READ
* wValue: 0
* wIndex: Address of word to read
* wLength: 2
* Data: Will return a word of data from E2Address
*
*/
/* Port Identifier Table */
#define PIT_DEFAULT 0 /* SIOA */
#define PIT_SIOA 1 /* SIOA */
/* The device this driver is tested with one has only one port */
#define PIT_SIOB 2 /* SIOB */
#define PIT_PARALLEL 3 /* Parallel */
/* FTDI_SIO_RESET */
#define FTDI_SIO_RESET_REQUEST FTDI_SIO_RESET
#define FTDI_SIO_RESET_REQUEST_TYPE 0x40
#define FTDI_SIO_RESET_SIO 0
#define FTDI_SIO_RESET_PURGE_RX 1
#define FTDI_SIO_RESET_PURGE_TX 2
/*
* BmRequestType: 0100 0000B
* bRequest: FTDI_SIO_RESET
* wValue: Control Value
* 0 = Reset SIO
* 1 = Purge RX buffer
* 2 = Purge TX buffer
* wIndex: Port
* wLength: 0
* Data: None
*
* The Reset SIO command has this effect:
*
* Sets flow control set to 'none'
* Event char = $0D
* Event trigger = disabled
* Purge RX buffer
* Purge TX buffer
* Clear DTR
* Clear RTS
* baud and data format not reset
*
* The Purge RX and TX buffer commands affect nothing except the buffers
*
*/
/* FTDI_SIO_SET_BAUDRATE */
#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40
#define FTDI_SIO_SET_BAUDRATE_REQUEST 3
/*
* BmRequestType: 0100 0000B
* bRequest: FTDI_SIO_SET_BAUDRATE
* wValue: BaudDivisor value - see below
* wIndex: Port
* wLength: 0
* Data: None
* The BaudDivisor values are calculated as follows:
* - BaseClock is either 12000000 or 48000000 depending on the device.
* FIXME: I wish I knew how to detect old chips to select proper base clock!
* - BaudDivisor is a fixed point number encoded in a funny way.
* (--WRONG WAY OF THINKING--)
* BaudDivisor is a fixed point number encoded with following bit weighs:
* (-2)(-1)(13..0). It is a radical with a denominator of 4, so values
* end with 0.0 (00...), 0.25 (10...), 0.5 (01...), and 0.75 (11...).
* (--THE REALITY--)
* The both-bits-set has quite different meaning from 0.75 - the chip
* designers have decided it to mean 0.125 instead of 0.75.
* This info looked up in FTDI application note "FT8U232 DEVICES \ Data Rates
* and Flow Control Consideration for USB to RS232".
* - BaudDivisor = (BaseClock / 16) / BaudRate, where the (=) operation should
* automagically re-encode the resulting value to take fractions into
* consideration.
* As all values are integers, some bit twiddling is in order:
* BaudDivisor = (BaseClock / 16 / BaudRate) |
* (((BaseClock / 2 / BaudRate) & 4) ? 0x4000 // 0.5
* : ((BaseClock / 2 / BaudRate) & 2) ? 0x8000 // 0.25
* : ((BaseClock / 2 / BaudRate) & 1) ? 0xc000 // 0.125
* : 0)
*
* For the FT232BM, a 17th divisor bit was introduced to encode the multiples
* of 0.125 missing from the FT8U232AM. Bits 16 to 14 are coded as follows
* (the first four codes are the same as for the FT8U232AM, where bit 16 is
* always 0):
* 000 - add .000 to divisor
* 001 - add .500 to divisor
* 010 - add .250 to divisor
* 011 - add .125 to divisor
* 100 - add .375 to divisor
* 101 - add .625 to divisor
* 110 - add .750 to divisor
* 111 - add .875 to divisor
* Bits 15 to 0 of the 17-bit divisor are placed in the urb value. Bit 16 is
* placed in bit 0 of the urb index.
*
* Note that there are a couple of special cases to support the highest baud
* rates. If the calculated divisor value is 1, this needs to be replaced with
* 0. Additionally for the FT232BM, if the calculated divisor value is 0x4001
* (1.5), this needs to be replaced with 0x0001 (1) (but this divisor value is
* not supported by the FT8U232AM).
*/
enum ftdi_chip_type {
SIO = 1,
FT8U232AM = 2,
FT232BM = 3,
FT2232C = 4,
FT232RL = 5,
FT2232H = 6,
FT4232H = 7,
FT232H = 8,
FTX = 9,
};
enum ftdi_sio_baudrate {
ftdi_sio_b300 = 0,
ftdi_sio_b600 = 1,
ftdi_sio_b1200 = 2,
ftdi_sio_b2400 = 3,
ftdi_sio_b4800 = 4,
ftdi_sio_b9600 = 5,
ftdi_sio_b19200 = 6,
ftdi_sio_b38400 = 7,
ftdi_sio_b57600 = 8,
ftdi_sio_b115200 = 9
};
/*
* The ftdi_8U232AM_xxMHz_byyy constants have been removed. The encoded divisor
* values are calculated internally.
*/
#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA
#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40
#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8)
#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8)
#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8)
#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8)
#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8)
#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11)
#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11)
#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11)
#define FTDI_SIO_SET_BREAK (0x1 << 14)
/* FTDI_SIO_SET_DATA */
/*
* BmRequestType: 0100 0000B
* bRequest: FTDI_SIO_SET_DATA
* wValue: Data characteristics (see below)
* wIndex: Port
* wLength: 0
* Data: No
*
* Data characteristics
*
* B0..7 Number of data bits
* B8..10 Parity
* 0 = None
* 1 = Odd
* 2 = Even
* 3 = Mark
* 4 = Space
* B11..13 Stop Bits
* 0 = 1
* 1 = 1.5
* 2 = 2
* B14
* 1 = TX ON (break)
* 0 = TX OFF (normal state)
* B15 Reserved
*
*/
/* FTDI_SIO_MODEM_CTRL */
#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40
#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL
/*
* BmRequestType: 0100 0000B
* bRequest: FTDI_SIO_MODEM_CTRL
* wValue: ControlValue (see below)
* wIndex: Port
* wLength: 0
* Data: None
*
* NOTE: If the device is in RTS/CTS flow control, the RTS set by this
* command will be IGNORED without an error being returned
* Also - you can not set DTR and RTS with one control message
*/
#define FTDI_SIO_SET_DTR_MASK 0x1
#define FTDI_SIO_SET_DTR_HIGH (1 | (FTDI_SIO_SET_DTR_MASK << 8))
#define FTDI_SIO_SET_DTR_LOW (0 | (FTDI_SIO_SET_DTR_MASK << 8))
#define FTDI_SIO_SET_RTS_MASK 0x2
#define FTDI_SIO_SET_RTS_HIGH (2 | (FTDI_SIO_SET_RTS_MASK << 8))
#define FTDI_SIO_SET_RTS_LOW (0 | (FTDI_SIO_SET_RTS_MASK << 8))
/*
* ControlValue
* B0 DTR state
* 0 = reset
* 1 = set
* B1 RTS state
* 0 = reset
* 1 = set
* B2..7 Reserved
* B8 DTR state enable
* 0 = ignore
* 1 = use DTR state
* B9 RTS state enable
* 0 = ignore
* 1 = use RTS state
* B10..15 Reserved
*/
/* FTDI_SIO_SET_FLOW_CTRL */
#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40
#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL
#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0
#define FTDI_SIO_RTS_CTS_HS (0x1 << 8)
#define FTDI_SIO_DTR_DSR_HS (0x2 << 8)
#define FTDI_SIO_XON_XOFF_HS (0x4 << 8)
/*
* BmRequestType: 0100 0000b
* bRequest: FTDI_SIO_SET_FLOW_CTRL
* wValue: Xoff/Xon
* wIndex: Protocol/Port - hIndex is protocol / lIndex is port
* wLength: 0
* Data: None
*
* hIndex protocol is:
* B0 Output handshaking using RTS/CTS
* 0 = disabled
* 1 = enabled
* B1 Output handshaking using DTR/DSR
* 0 = disabled
* 1 = enabled
* B2 Xon/Xoff handshaking
* 0 = disabled
* 1 = enabled
*
* A value of zero in the hIndex field disables handshaking
*
* If Xon/Xoff handshaking is specified, the hValue field should contain the
* XOFF character and the lValue field contains the XON character.
*/
/*
* FTDI_SIO_GET_LATENCY_TIMER
*
* Set the timeout interval. The FTDI collects data from the slave
* device, transmitting it to the host when either A) 62 bytes are
* received, or B) the timeout interval has elapsed and the buffer
* contains at least 1 byte. Setting this value to a small number
* can dramatically improve performance for applications which send
* small packets, since the default value is 16ms.
*/
#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER
#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0
/*
* BmRequestType: 1100 0000b
* bRequest: FTDI_SIO_GET_LATENCY_TIMER
* wValue: 0
* wIndex: Port
* wLength: 0
* Data: latency (on return)
*/
/*
* FTDI_SIO_SET_LATENCY_TIMER
*
* Set the timeout interval. The FTDI collects data from the slave
* device, transmitting it to the host when either A) 62 bytes are
* received, or B) the timeout interval has elapsed and the buffer
* contains at least 1 byte. Setting this value to a small number
* can dramatically improve performance for applications which send
* small packets, since the default value is 16ms.
*/
#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER
#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40
/*
* BmRequestType: 0100 0000b
* bRequest: FTDI_SIO_SET_LATENCY_TIMER
* wValue: Latency (milliseconds)
* wIndex: Port
* wLength: 0
* Data: None
*
* wValue:
* B0..7 Latency timer
* B8..15 0
*
*/
/*
* FTDI_SIO_SET_EVENT_CHAR
*
* Set the special event character for the specified communications port.
* If the device sees this character it will immediately return the
* data read so far - rather than wait 40ms or until 62 bytes are read
* which is what normally happens.
*/
#define FTDI_SIO_SET_EVENT_CHAR_REQUEST FTDI_SIO_SET_EVENT_CHAR
#define FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40
/*
* BmRequestType: 0100 0000b
* bRequest: FTDI_SIO_SET_EVENT_CHAR
* wValue: EventChar
* wIndex: Port
* wLength: 0
* Data: None
*
* wValue:
* B0..7 Event Character
* B8 Event Character Processing
* 0 = disabled
* 1 = enabled
* B9..15 Reserved
*
*/
/* FTDI_SIO_SET_ERROR_CHAR */
/*
* Set the parity error replacement character for the specified communications
* port
*/
/*
* BmRequestType: 0100 0000b
* bRequest: FTDI_SIO_SET_EVENT_CHAR
* wValue: Error Char
* wIndex: Port
* wLength: 0
* Data: None
*
*Error Char
* B0..7 Error Character
* B8 Error Character Processing
* 0 = disabled
* 1 = enabled
* B9..15 Reserved
*
*/
/* FTDI_SIO_GET_MODEM_STATUS */
/* Retrieve the current value of the modem status register */
#define FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE 0xc0
#define FTDI_SIO_GET_MODEM_STATUS_REQUEST FTDI_SIO_GET_MODEM_STATUS
#define FTDI_SIO_CTS_MASK 0x10
#define FTDI_SIO_DSR_MASK 0x20
#define FTDI_SIO_RI_MASK 0x40
#define FTDI_SIO_RLSD_MASK 0x80
/*
* BmRequestType: 1100 0000b
* bRequest: FTDI_SIO_GET_MODEM_STATUS
* wValue: zero
* wIndex: Port
* wLength: 1
* Data: Status
*
* One byte of data is returned
* B0..3 0
* B4 CTS
* 0 = inactive
* 1 = active
* B5 DSR
* 0 = inactive
* 1 = active
* B6 Ring Indicator (RI)
* 0 = inactive
* 1 = active
* B7 Receive Line Signal Detect (RLSD)
* 0 = inactive
* 1 = active
*/
/* Descriptors returned by the device
*
* Device Descriptor
*
* Offset Field Size Value Description
* 0 bLength 1 0x12 Size of descriptor in bytes
* 1 bDescriptorType 1 0x01 DEVICE Descriptor Type
* 2 bcdUSB 2 0x0110 USB Spec Release Number
* 4 bDeviceClass 1 0x00 Class Code
* 5 bDeviceSubClass 1 0x00 SubClass Code
* 6 bDeviceProtocol 1 0x00 Protocol Code
* 7 bMaxPacketSize0 1 0x08 Maximum packet size for endpoint 0
* 8 idVendor 2 0x0403 Vendor ID
* 10 idProduct 2 0x8372 Product ID (FTDI_SIO_PID)
* 12 bcdDevice 2 0x0001 Device release number
* 14 iManufacturer 1 0x01 Index of man. string desc
* 15 iProduct 1 0x02 Index of prod string desc
* 16 iSerialNumber 1 0x02 Index of serial nmr string desc
* 17 bNumConfigurations 1 0x01 Number of possible configurations
*
* Configuration Descriptor
*
* Offset Field Size Value
* 0 bLength 1 0x09 Size of descriptor in bytes
* 1 bDescriptorType 1 0x02 CONFIGURATION Descriptor Type
* 2 wTotalLength 2 0x0020 Total length of data
* 4 bNumInterfaces 1 0x01 Number of interfaces supported
* 5 bConfigurationValue 1 0x01 Argument for SetCOnfiguration() req
* 6 iConfiguration 1 0x02 Index of config string descriptor
* 7 bmAttributes 1 0x20 Config characteristics Remote Wakeup
* 8 MaxPower 1 0x1E Max power consumption
*
* Interface Descriptor
*
* Offset Field Size Value
* 0 bLength 1 0x09 Size of descriptor in bytes
* 1 bDescriptorType 1 0x04 INTERFACE Descriptor Type
* 2 bInterfaceNumber 1 0x00 Number of interface
* 3 bAlternateSetting 1 0x00 Value used to select alternate
* 4 bNumEndpoints 1 0x02 Number of endpoints
* 5 bInterfaceClass 1 0xFF Class Code
* 6 bInterfaceSubClass 1 0xFF Subclass Code
* 7 bInterfaceProtocol 1 0xFF Protocol Code
* 8 iInterface 1 0x02 Index of interface string description
*
* IN Endpoint Descriptor
*
* Offset Field Size Value
* 0 bLength 1 0x07 Size of descriptor in bytes
* 1 bDescriptorType 1 0x05 ENDPOINT descriptor type
* 2 bEndpointAddress 1 0x82 Address of endpoint
* 3 bmAttributes 1 0x02 Endpoint attributes - Bulk
* 4 bNumEndpoints 2 0x0040 maximum packet size
* 5 bInterval 1 0x00 Interval for polling endpoint
*
* OUT Endpoint Descriptor
*
* Offset Field Size Value
* 0 bLength 1 0x07 Size of descriptor in bytes
* 1 bDescriptorType 1 0x05 ENDPOINT descriptor type
* 2 bEndpointAddress 1 0x02 Address of endpoint
* 3 bmAttributes 1 0x02 Endpoint attributes - Bulk
* 4 bNumEndpoints 2 0x0040 maximum packet size
* 5 bInterval 1 0x00 Interval for polling endpoint
*
* DATA FORMAT
*
* IN Endpoint
*
* The device reserves the first two bytes of data on this endpoint to contain
* the current values of the modem and line status registers. In the absence of
* data, the device generates a message consisting of these two status bytes
* every 40 ms
*
* Byte 0: Modem Status
*
* Offset Description
* B0 Reserved - must be 1
* B1 Reserved - must be 0
* B2 Reserved - must be 0
* B3 Reserved - must be 0
* B4 Clear to Send (CTS)
* B5 Data Set Ready (DSR)
* B6 Ring Indicator (RI)
* B7 Receive Line Signal Detect (RLSD)
*
* Byte 1: Line Status
*
* Offset Description
* B0 Data Ready (DR)
* B1 Overrun Error (OE)
* B2 Parity Error (PE)
* B3 Framing Error (FE)
* B4 Break Interrupt (BI)
* B5 Transmitter Holding Register (THRE)
* B6 Transmitter Empty (TEMT)
* B7 Error in RCVR FIFO
*
*/
#define FTDI_RS0_CTS (1 << 4)
#define FTDI_RS0_DSR (1 << 5)
#define FTDI_RS0_RI (1 << 6)
#define FTDI_RS0_RLSD (1 << 7)
#define FTDI_RS_DR 1
#define FTDI_RS_OE (1<<1)
#define FTDI_RS_PE (1<<2)
#define FTDI_RS_FE (1<<3)
#define FTDI_RS_BI (1<<4)
#define FTDI_RS_THRE (1<<5)
#define FTDI_RS_TEMT (1<<6)
#define FTDI_RS_FIFO (1<<7)
/*
* OUT Endpoint
*
* This device reserves the first bytes of data on this endpoint contain the
* length and port identifier of the message. For the FTDI USB Serial converter
* the port identifier is always 1.
*
* Byte 0: Line Status
*
* Offset Description
* B0 Reserved - must be 1
* B1 Reserved - must be 0
* B2..7 Length of message - (not including Byte 0)
*
*/

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,628 @@
/*
* USB Serial Converter Generic functions
*
* Copyright (C) 2010 - 2013 Johan Hovold (jhovold@gmail.com)
* Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/sysrq.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
#include <linux/kfifo.h>
#include <linux/serial.h>
#ifdef CONFIG_USB_SERIAL_GENERIC
static __u16 vendor = 0x05f9;
static __u16 product = 0xffff;
module_param(vendor, ushort, 0);
MODULE_PARM_DESC(vendor, "User specified USB idVendor");
module_param(product, ushort, 0);
MODULE_PARM_DESC(product, "User specified USB idProduct");
static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
struct usb_serial_driver usb_serial_generic_device = {
.driver = {
.owner = THIS_MODULE,
.name = "generic",
},
.id_table = generic_device_ids,
.num_ports = 1,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
.resume = usb_serial_generic_resume,
};
static struct usb_serial_driver * const serial_drivers[] = {
&usb_serial_generic_device, NULL
};
#endif
int usb_serial_generic_register(void)
{
int retval = 0;
#ifdef CONFIG_USB_SERIAL_GENERIC
generic_device_ids[0].idVendor = vendor;
generic_device_ids[0].idProduct = product;
generic_device_ids[0].match_flags =
USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
retval = usb_serial_register_drivers(serial_drivers,
"usbserial_generic", generic_device_ids);
#endif
return retval;
}
void usb_serial_generic_deregister(void)
{
#ifdef CONFIG_USB_SERIAL_GENERIC
usb_serial_deregister_drivers(serial_drivers);
#endif
}
int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port)
{
int result = 0;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
port->throttled = 0;
port->throttle_req = 0;
spin_unlock_irqrestore(&port->lock, flags);
if (port->bulk_in_size)
result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
return result;
}
EXPORT_SYMBOL_GPL(usb_serial_generic_open);
void usb_serial_generic_close(struct usb_serial_port *port)
{
unsigned long flags;
int i;
if (port->bulk_out_size) {
for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
usb_kill_urb(port->write_urbs[i]);
spin_lock_irqsave(&port->lock, flags);
kfifo_reset_out(&port->write_fifo);
spin_unlock_irqrestore(&port->lock, flags);
}
if (port->bulk_in_size) {
for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i)
usb_kill_urb(port->read_urbs[i]);
}
}
EXPORT_SYMBOL_GPL(usb_serial_generic_close);
int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
void *dest, size_t size)
{
return kfifo_out_locked(&port->write_fifo, dest, size, &port->lock);
}
/**
* usb_serial_generic_write_start - start writing buffered data
* @port: usb-serial port
* @mem_flags: flags to use for memory allocations
*
* Serialised using USB_SERIAL_WRITE_BUSY flag.
*
* Return: Zero on success or if busy, otherwise a negative errno value.
*/
int usb_serial_generic_write_start(struct usb_serial_port *port,
gfp_t mem_flags)
{
struct urb *urb;
int count, result;
unsigned long flags;
int i;
if (test_and_set_bit_lock(USB_SERIAL_WRITE_BUSY, &port->flags))
return 0;
retry:
spin_lock_irqsave(&port->lock, flags);
if (!port->write_urbs_free || !kfifo_len(&port->write_fifo)) {
clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
spin_unlock_irqrestore(&port->lock, flags);
return 0;
}
i = (int)find_first_bit(&port->write_urbs_free,
ARRAY_SIZE(port->write_urbs));
spin_unlock_irqrestore(&port->lock, flags);
urb = port->write_urbs[i];
count = port->serial->type->prepare_write_buffer(port,
urb->transfer_buffer,
port->bulk_out_size);
urb->transfer_buffer_length = count;
usb_serial_debug_data(&port->dev, __func__, count, urb->transfer_buffer);
spin_lock_irqsave(&port->lock, flags);
port->tx_bytes += count;
spin_unlock_irqrestore(&port->lock, flags);
clear_bit(i, &port->write_urbs_free);
result = usb_submit_urb(urb, mem_flags);
if (result) {
dev_err_console(port, "%s - error submitting urb: %d\n",
__func__, result);
set_bit(i, &port->write_urbs_free);
spin_lock_irqsave(&port->lock, flags);
port->tx_bytes -= count;
spin_unlock_irqrestore(&port->lock, flags);
clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
return result;
}
goto retry; /* try sending off another urb */
}
EXPORT_SYMBOL_GPL(usb_serial_generic_write_start);
/**
* usb_serial_generic_write - generic write function
* @tty: tty for the port
* @port: usb-serial port
* @buf: data to write
* @count: number of bytes to write
*
* Return: The number of characters buffered, which may be anything from
* zero to @count, or a negative errno value.
*/
int usb_serial_generic_write(struct tty_struct *tty,
struct usb_serial_port *port, const unsigned char *buf, int count)
{
int result;
if (!port->bulk_out_size)
return -ENODEV;
if (!count)
return 0;
count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
result = usb_serial_generic_write_start(port, GFP_ATOMIC);
if (result)
return result;
return count;
}
EXPORT_SYMBOL_GPL(usb_serial_generic_write);
int usb_serial_generic_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
unsigned long flags;
int room;
if (!port->bulk_out_size)
return 0;
spin_lock_irqsave(&port->lock, flags);
room = kfifo_avail(&port->write_fifo);
spin_unlock_irqrestore(&port->lock, flags);
dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
return room;
}
int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
unsigned long flags;
int chars;
if (!port->bulk_out_size)
return 0;
spin_lock_irqsave(&port->lock, flags);
chars = kfifo_len(&port->write_fifo) + port->tx_bytes;
spin_unlock_irqrestore(&port->lock, flags);
dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
return chars;
}
EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer);
void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout)
{
struct usb_serial_port *port = tty->driver_data;
unsigned int bps;
unsigned long period;
unsigned long expire;
bps = tty_get_baud_rate(tty);
if (!bps)
bps = 9600; /* B0 */
/*
* Use a poll-period of roughly the time it takes to send one
* character or at least one jiffy.
*/
period = max_t(unsigned long, (10 * HZ / bps), 1);
if (timeout)
period = min_t(unsigned long, period, timeout);
dev_dbg(&port->dev, "%s - timeout = %u ms, period = %u ms\n",
__func__, jiffies_to_msecs(timeout),
jiffies_to_msecs(period));
expire = jiffies + timeout;
while (!port->serial->type->tx_empty(port)) {
schedule_timeout_interruptible(period);
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, expire))
break;
}
}
EXPORT_SYMBOL_GPL(usb_serial_generic_wait_until_sent);
static int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
int index, gfp_t mem_flags)
{
int res;
if (!test_and_clear_bit(index, &port->read_urbs_free))
return 0;
dev_dbg(&port->dev, "%s - urb %d\n", __func__, index);
res = usb_submit_urb(port->read_urbs[index], mem_flags);
if (res) {
if (res != -EPERM) {
dev_err(&port->dev,
"%s - usb_submit_urb failed: %d\n",
__func__, res);
}
set_bit(index, &port->read_urbs_free);
return res;
}
return 0;
}
int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port,
gfp_t mem_flags)
{
int res;
int i;
for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
res = usb_serial_generic_submit_read_urb(port, i, mem_flags);
if (res)
goto err;
}
return 0;
err:
for (; i >= 0; --i)
usb_kill_urb(port->read_urbs[i]);
return res;
}
EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urbs);
void usb_serial_generic_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
char *ch = (char *)urb->transfer_buffer;
int i;
if (!urb->actual_length)
return;
/*
* The per character mucking around with sysrq path it too slow for
* stuff like 3G modems, so shortcircuit it in the 99.9999999% of
* cases where the USB serial is not a console anyway.
*/
if (!port->port.console || !port->sysrq) {
tty_insert_flip_string(&port->port, ch, urb->actual_length);
} else {
for (i = 0; i < urb->actual_length; i++, ch++) {
if (!usb_serial_handle_sysrq_char(port, *ch))
tty_insert_flip_char(&port->port, *ch, TTY_NORMAL);
}
}
tty_flip_buffer_push(&port->port);
}
EXPORT_SYMBOL_GPL(usb_serial_generic_process_read_urb);
void usb_serial_generic_read_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
unsigned long flags;
int i;
for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
if (urb == port->read_urbs[i])
break;
}
set_bit(i, &port->read_urbs_free);
dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i,
urb->actual_length);
switch (urb->status) {
case 0:
break;
case -ENOENT:
case -ECONNRESET:
case -ESHUTDOWN:
dev_dbg(&port->dev, "%s - urb stopped: %d\n",
__func__, urb->status);
return;
case -EPIPE:
dev_err(&port->dev, "%s - urb stopped: %d\n",
__func__, urb->status);
return;
default:
dev_dbg(&port->dev, "%s - nonzero urb status: %d\n",
__func__, urb->status);
goto resubmit;
}
usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
port->serial->type->process_read_urb(urb);
resubmit:
/* Throttle the device if requested by tty */
spin_lock_irqsave(&port->lock, flags);
port->throttled = port->throttle_req;
if (!port->throttled) {
spin_unlock_irqrestore(&port->lock, flags);
usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC);
} else {
spin_unlock_irqrestore(&port->lock, flags);
}
}
EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);
void usb_serial_generic_write_bulk_callback(struct urb *urb)
{
unsigned long flags;
struct usb_serial_port *port = urb->context;
int i;
for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) {
if (port->write_urbs[i] == urb)
break;
}
spin_lock_irqsave(&port->lock, flags);
port->tx_bytes -= urb->transfer_buffer_length;
set_bit(i, &port->write_urbs_free);
spin_unlock_irqrestore(&port->lock, flags);
switch (urb->status) {
case 0:
break;
case -ENOENT:
case -ECONNRESET:
case -ESHUTDOWN:
dev_dbg(&port->dev, "%s - urb stopped: %d\n",
__func__, urb->status);
return;
case -EPIPE:
dev_err_console(port, "%s - urb stopped: %d\n",
__func__, urb->status);
return;
default:
dev_err_console(port, "%s - nonzero urb status: %d\n",
__func__, urb->status);
goto resubmit;
}
resubmit:
usb_serial_generic_write_start(port, GFP_ATOMIC);
usb_serial_port_softint(port);
}
EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback);
void usb_serial_generic_throttle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
port->throttle_req = 1;
spin_unlock_irqrestore(&port->lock, flags);
}
EXPORT_SYMBOL_GPL(usb_serial_generic_throttle);
void usb_serial_generic_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
int was_throttled;
spin_lock_irq(&port->lock);
was_throttled = port->throttled;
port->throttled = port->throttle_req = 0;
spin_unlock_irq(&port->lock);
if (was_throttled)
usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
}
EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle);
static bool usb_serial_generic_msr_changed(struct tty_struct *tty,
unsigned long arg, struct async_icount *cprev)
{
struct usb_serial_port *port = tty->driver_data;
struct async_icount cnow;
unsigned long flags;
bool ret;
/*
* Use tty-port initialised flag to detect all hangups including the
* one generated at USB-device disconnect.
*/
if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags))
return true;
spin_lock_irqsave(&port->lock, flags);
cnow = port->icount; /* atomic copy*/
spin_unlock_irqrestore(&port->lock, flags);
ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) ||
((arg & TIOCM_CTS) && (cnow.cts != cprev->cts));
*cprev = cnow;
return ret;
}
int usb_serial_generic_tiocmiwait(struct tty_struct *tty, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
struct async_icount cnow;
unsigned long flags;
int ret;
spin_lock_irqsave(&port->lock, flags);
cnow = port->icount; /* atomic copy */
spin_unlock_irqrestore(&port->lock, flags);
ret = wait_event_interruptible(port->port.delta_msr_wait,
usb_serial_generic_msr_changed(tty, arg, &cnow));
if (!ret && !test_bit(ASYNCB_INITIALIZED, &port->port.flags))
ret = -EIO;
return ret;
}
EXPORT_SYMBOL_GPL(usb_serial_generic_tiocmiwait);
int usb_serial_generic_get_icount(struct tty_struct *tty,
struct serial_icounter_struct *icount)
{
struct usb_serial_port *port = tty->driver_data;
struct async_icount cnow;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
cnow = port->icount; /* atomic copy */
spin_unlock_irqrestore(&port->lock, flags);
icount->cts = cnow.cts;
icount->dsr = cnow.dsr;
icount->rng = cnow.rng;
icount->dcd = cnow.dcd;
icount->tx = cnow.tx;
icount->rx = cnow.rx;
icount->frame = cnow.frame;
icount->parity = cnow.parity;
icount->overrun = cnow.overrun;
icount->brk = cnow.brk;
icount->buf_overrun = cnow.buf_overrun;
return 0;
}
EXPORT_SYMBOL_GPL(usb_serial_generic_get_icount);
#ifdef CONFIG_MAGIC_SYSRQ
int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch)
{
if (port->sysrq && port->port.console) {
if (ch && time_before(jiffies, port->sysrq)) {
handle_sysrq(ch);
port->sysrq = 0;
return 1;
}
port->sysrq = 0;
}
return 0;
}
#else
int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch)
{
return 0;
}
#endif
EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char);
int usb_serial_handle_break(struct usb_serial_port *port)
{
if (!port->sysrq) {
port->sysrq = jiffies + HZ*5;
return 1;
}
port->sysrq = 0;
return 0;
}
EXPORT_SYMBOL_GPL(usb_serial_handle_break);
/**
* usb_serial_handle_dcd_change - handle a change of carrier detect state
* @port: usb-serial port
* @tty: tty for the port
* @status: new carrier detect status, nonzero if active
*/
void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
struct tty_struct *tty, unsigned int status)
{
struct tty_port *port = &usb_port->port;
dev_dbg(&usb_port->dev, "%s - status %d\n", __func__, status);
if (tty) {
struct tty_ldisc *ld = tty_ldisc_ref(tty);
if (ld) {
if (ld->ops->dcd_change)
ld->ops->dcd_change(tty, status);
tty_ldisc_deref(ld);
}
}
if (status)
wake_up_interruptible(&port->open_wait);
else if (tty && !C_CLOCAL(tty))
tty_hangup(tty);
}
EXPORT_SYMBOL_GPL(usb_serial_handle_dcd_change);
int usb_serial_generic_resume(struct usb_serial *serial)
{
struct usb_serial_port *port;
int i, c = 0, r;
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags))
continue;
if (port->bulk_in_size) {
r = usb_serial_generic_submit_read_urbs(port,
GFP_NOIO);
if (r < 0)
c++;
}
if (port->bulk_out_size) {
r = usb_serial_generic_write_start(port, GFP_NOIO);
if (r < 0)
c++;
}
}
return c ? -EIO : 0;
}
EXPORT_SYMBOL_GPL(usb_serial_generic_resume);

View file

@ -0,0 +1,195 @@
/************************************************************************
*
* 16654.H Definitions for 16C654 UART used on EdgePorts
*
* Copyright (C) 1998 Inside Out Networks, Inc.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
************************************************************************/
#if !defined(_16654_H)
#define _16654_H
/************************************************************************
*
* D e f i n e s / T y p e d e f s
*
************************************************************************/
//
// UART register numbers
// Numbers 0-7 are passed to the Edgeport directly. Numbers 8 and
// above are used internally to indicate that we must enable access
// to them via LCR bit 0x80 or LCR = 0xBF.
// The register number sent to the Edgeport is then (x & 0x7).
//
// Driver must not access registers that affect operation of the
// the EdgePort firmware -- that includes THR, RHR, IER, FCR.
#define THR 0 // ! Transmit Holding Register (Write)
#define RDR 0 // ! Receive Holding Register (Read)
#define IER 1 // ! Interrupt Enable Register
#define FCR 2 // ! Fifo Control Register (Write)
#define ISR 2 // Interrupt Status Register (Read)
#define LCR 3 // Line Control Register
#define MCR 4 // Modem Control Register
#define LSR 5 // Line Status Register
#define MSR 6 // Modem Status Register
#define SPR 7 // ScratchPad Register
#define DLL 8 // Bank2[ 0 ] Divisor Latch LSB
#define DLM 9 // Bank2[ 1 ] Divisor Latch MSB
#define EFR 10 // Bank2[ 2 ] Extended Function Register
//efine unused 11 // Bank2[ 3 ]
#define XON1 12 // Bank2[ 4 ] Xon-1
#define XON2 13 // Bank2[ 5 ] Xon-2
#define XOFF1 14 // Bank2[ 6 ] Xoff-1
#define XOFF2 15 // Bank2[ 7 ] Xoff-2
#define NUM_16654_REGS 16
#define IS_REG_2ND_BANK(x) ((x) >= 8)
//
// Bit definitions for each register
//
#define IER_RX 0x01 // Enable receive interrupt
#define IER_TX 0x02 // Enable transmit interrupt
#define IER_RXS 0x04 // Enable receive status interrupt
#define IER_MDM 0x08 // Enable modem status interrupt
#define IER_SLEEP 0x10 // Enable sleep mode
#define IER_XOFF 0x20 // Enable s/w flow control (XOFF) interrupt
#define IER_RTS 0x40 // Enable RTS interrupt
#define IER_CTS 0x80 // Enable CTS interrupt
#define IER_ENABLE_ALL 0xFF // Enable all ints
#define FCR_FIFO_EN 0x01 // Enable FIFOs
#define FCR_RXCLR 0x02 // Reset Rx FIFO
#define FCR_TXCLR 0x04 // Reset Tx FIFO
#define FCR_DMA_BLK 0x08 // Enable DMA block mode
#define FCR_TX_LEVEL_MASK 0x30 // Mask for Tx FIFO Level
#define FCR_TX_LEVEL_8 0x00 // Tx FIFO Level = 8 bytes
#define FCR_TX_LEVEL_16 0x10 // Tx FIFO Level = 16 bytes
#define FCR_TX_LEVEL_32 0x20 // Tx FIFO Level = 32 bytes
#define FCR_TX_LEVEL_56 0x30 // Tx FIFO Level = 56 bytes
#define FCR_RX_LEVEL_MASK 0xC0 // Mask for Rx FIFO Level
#define FCR_RX_LEVEL_8 0x00 // Rx FIFO Level = 8 bytes
#define FCR_RX_LEVEL_16 0x40 // Rx FIFO Level = 16 bytes
#define FCR_RX_LEVEL_56 0x80 // Rx FIFO Level = 56 bytes
#define FCR_RX_LEVEL_60 0xC0 // Rx FIFO Level = 60 bytes
#define ISR_INT_MDM_STATUS 0x00 // Modem status int pending
#define ISR_INT_NONE 0x01 // No interrupt pending
#define ISR_INT_TXRDY 0x02 // Tx ready int pending
#define ISR_INT_RXRDY 0x04 // Rx ready int pending
#define ISR_INT_LINE_STATUS 0x06 // Line status int pending
#define ISR_INT_RX_TIMEOUT 0x0C // Rx timeout int pending
#define ISR_INT_RX_XOFF 0x10 // Rx Xoff int pending
#define ISR_INT_RTS_CTS 0x20 // RTS/CTS change int pending
#define ISR_FIFO_ENABLED 0xC0 // Bits set if FIFOs enabled
#define ISR_INT_BITS_MASK 0x3E // Mask to isolate valid int causes
#define LCR_BITS_5 0x00 // 5 bits/char
#define LCR_BITS_6 0x01 // 6 bits/char
#define LCR_BITS_7 0x02 // 7 bits/char
#define LCR_BITS_8 0x03 // 8 bits/char
#define LCR_BITS_MASK 0x03 // Mask for bits/char field
#define LCR_STOP_1 0x00 // 1 stop bit
#define LCR_STOP_1_5 0x04 // 1.5 stop bits (if 5 bits/char)
#define LCR_STOP_2 0x04 // 2 stop bits (if 6-8 bits/char)
#define LCR_STOP_MASK 0x04 // Mask for stop bits field
#define LCR_PAR_NONE 0x00 // No parity
#define LCR_PAR_ODD 0x08 // Odd parity
#define LCR_PAR_EVEN 0x18 // Even parity
#define LCR_PAR_MARK 0x28 // Force parity bit to 1
#define LCR_PAR_SPACE 0x38 // Force parity bit to 0
#define LCR_PAR_MASK 0x38 // Mask for parity field
#define LCR_SET_BREAK 0x40 // Set Break condition
#define LCR_DL_ENABLE 0x80 // Enable access to divisor latch
#define LCR_ACCESS_EFR 0xBF // Load this value to access DLL,DLM,
// and also the '654-only registers
// EFR, XON1, XON2, XOFF1, XOFF2
#define MCR_DTR 0x01 // Assert DTR
#define MCR_RTS 0x02 // Assert RTS
#define MCR_OUT1 0x04 // Loopback only: Sets state of RI
#define MCR_MASTER_IE 0x08 // Enable interrupt outputs
#define MCR_LOOPBACK 0x10 // Set internal (digital) loopback mode
#define MCR_XON_ANY 0x20 // Enable any char to exit XOFF mode
#define MCR_IR_ENABLE 0x40 // Enable IrDA functions
#define MCR_BRG_DIV_4 0x80 // Divide baud rate clk by /4 instead of /1
#define LSR_RX_AVAIL 0x01 // Rx data available
#define LSR_OVER_ERR 0x02 // Rx overrun
#define LSR_PAR_ERR 0x04 // Rx parity error
#define LSR_FRM_ERR 0x08 // Rx framing error
#define LSR_BREAK 0x10 // Rx break condition detected
#define LSR_TX_EMPTY 0x20 // Tx Fifo empty
#define LSR_TX_ALL_EMPTY 0x40 // Tx Fifo and shift register empty
#define LSR_FIFO_ERR 0x80 // Rx Fifo contains at least 1 erred char
#define EDGEPORT_MSR_DELTA_CTS 0x01 // CTS changed from last read
#define EDGEPORT_MSR_DELTA_DSR 0x02 // DSR changed from last read
#define EDGEPORT_MSR_DELTA_RI 0x04 // RI changed from 0 -> 1
#define EDGEPORT_MSR_DELTA_CD 0x08 // CD changed from last read
#define EDGEPORT_MSR_CTS 0x10 // Current state of CTS
#define EDGEPORT_MSR_DSR 0x20 // Current state of DSR
#define EDGEPORT_MSR_RI 0x40 // Current state of RI
#define EDGEPORT_MSR_CD 0x80 // Current state of CD
// Tx Rx
//-------------------------------
#define EFR_SWFC_NONE 0x00 // None None
#define EFR_SWFC_RX1 0x02 // None XOFF1
#define EFR_SWFC_RX2 0x01 // None XOFF2
#define EFR_SWFC_RX12 0x03 // None XOFF1 & XOFF2
#define EFR_SWFC_TX1 0x08 // XOFF1 None
#define EFR_SWFC_TX1_RX1 0x0a // XOFF1 XOFF1
#define EFR_SWFC_TX1_RX2 0x09 // XOFF1 XOFF2
#define EFR_SWFC_TX1_RX12 0x0b // XOFF1 XOFF1 & XOFF2
#define EFR_SWFC_TX2 0x04 // XOFF2 None
#define EFR_SWFC_TX2_RX1 0x06 // XOFF2 XOFF1
#define EFR_SWFC_TX2_RX2 0x05 // XOFF2 XOFF2
#define EFR_SWFC_TX2_RX12 0x07 // XOFF2 XOFF1 & XOFF2
#define EFR_SWFC_TX12 0x0c // XOFF1 & XOFF2 None
#define EFR_SWFC_TX12_RX1 0x0e // XOFF1 & XOFF2 XOFF1
#define EFR_SWFC_TX12_RX2 0x0d // XOFF1 & XOFF2 XOFF2
#define EFR_SWFC_TX12_RX12 0x0f // XOFF1 & XOFF2 XOFF1 & XOFF2
#define EFR_TX_FC_MASK 0x0c // Mask to isolate Rx flow control
#define EFR_TX_FC_NONE 0x00 // No Tx Xon/Xoff flow control
#define EFR_TX_FC_X1 0x08 // Transmit Xon1/Xoff1
#define EFR_TX_FC_X2 0x04 // Transmit Xon2/Xoff2
#define EFR_TX_FC_X1_2 0x0c // Transmit Xon1&2/Xoff1&2
#define EFR_RX_FC_MASK 0x03 // Mask to isolate Rx flow control
#define EFR_RX_FC_NONE 0x00 // No Rx Xon/Xoff flow control
#define EFR_RX_FC_X1 0x02 // Receiver compares Xon1/Xoff1
#define EFR_RX_FC_X2 0x01 // Receiver compares Xon2/Xoff2
#define EFR_RX_FC_X1_2 0x03 // Receiver compares Xon1&2/Xoff1&2
#define EFR_SWFC_MASK 0x0F // Mask for software flow control field
#define EFR_ENABLE_16654 0x10 // Enable 16C654 features
#define EFR_SPEC_DETECT 0x20 // Enable special character detect interrupt
#define EFR_AUTO_RTS 0x40 // Use RTS for Rx flow control
#define EFR_AUTO_CTS 0x80 // Use CTS for Tx flow control
#endif // if !defined(_16654_H)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,134 @@
/************************************************************************
*
* io_edgeport.h Edgeport Linux Interface definitions
*
* Copyright (C) 2000 Inside Out Networks, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*
************************************************************************/
#if !defined(_IO_EDGEPORT_H_)
#define _IO_EDGEPORT_H_
#define MAX_RS232_PORTS 8 /* Max # of RS-232 ports per device */
/* typedefs that the insideout headers need */
#ifndef LOW8
#define LOW8(a) ((unsigned char)(a & 0xff))
#endif
#ifndef HIGH8
#define HIGH8(a) ((unsigned char)((a & 0xff00) >> 8))
#endif
#ifndef __KERNEL__
#define __KERNEL__
#endif
#include "io_usbvend.h"
/* The following table is used to map the USBx port number to
* the device serial number (or physical USB path), */
#define MAX_EDGEPORTS 64
struct comMapper {
char SerialNumber[MAX_SERIALNUMBER_LEN+1]; /* Serial number/usb path */
int numPorts; /* Number of ports */
int Original[MAX_RS232_PORTS]; /* Port numbers set by IOCTL */
int Port[MAX_RS232_PORTS]; /* Actual used port numbers */
};
#define EDGEPORT_CONFIG_DEVICE "/proc/edgeport"
/* /proc/edgeport Interface
* This interface uses read/write/lseek interface to talk to the edgeport driver
* the following read functions are supported: */
#define PROC_GET_MAPPING_TO_PATH 1
#define PROC_GET_COM_ENTRY 2
#define PROC_GET_EDGE_MANUF_DESCRIPTOR 3
#define PROC_GET_BOOT_DESCRIPTOR 4
#define PROC_GET_PRODUCT_INFO 5
#define PROC_GET_STRINGS 6
#define PROC_GET_CURRENT_COM_MAPPING 7
/* The parameters to the lseek() for the read is: */
#define PROC_READ_SETUP(Command, Argument) ((Command) + ((Argument)<<8))
/* the following write functions are supported: */
#define PROC_SET_COM_MAPPING 1
#define PROC_SET_COM_ENTRY 2
/* The following structure is passed to the write */
struct procWrite {
int Command;
union {
struct comMapper Entry;
int ComMappingBasedOnUSBPort; /* Boolean value */
} u;
};
/*
* Product information read from the Edgeport
*/
struct edgeport_product_info {
__u16 ProductId; /* Product Identifier */
__u8 NumPorts; /* Number of ports on edgeport */
__u8 ProdInfoVer; /* What version of structure is this? */
__u32 IsServer :1; /* Set if Server */
__u32 IsRS232 :1; /* Set if RS-232 ports exist */
__u32 IsRS422 :1; /* Set if RS-422 ports exist */
__u32 IsRS485 :1; /* Set if RS-485 ports exist */
__u32 IsReserved :28; /* Reserved for later expansion */
__u8 RomSize; /* Size of ROM/E2PROM in K */
__u8 RamSize; /* Size of external RAM in K */
__u8 CpuRev; /* CPU revision level (chg only if s/w visible) */
__u8 BoardRev; /* PCB revision level (chg only if s/w visible) */
__u8 BootMajorVersion; /* Boot Firmware version: xx. */
__u8 BootMinorVersion; /* yy. */
__le16 BootBuildNumber; /* zzzz (LE format) */
__u8 FirmwareMajorVersion; /* Operational Firmware version:xx. */
__u8 FirmwareMinorVersion; /* yy. */
__le16 FirmwareBuildNumber; /* zzzz (LE format) */
__u8 ManufactureDescDate[3]; /* MM/DD/YY when descriptor template was compiled */
__u8 HardwareType;
__u8 iDownloadFile; /* What to download to EPiC device */
__u8 EpicVer; /* What version of EPiC spec this device supports */
struct edge_compatibility_bits Epic;
};
/*
* Edgeport Stringblock String locations
*/
#define EDGESTRING_MANUFNAME 1 /* Manufacture Name */
#define EDGESTRING_PRODNAME 2 /* Product Name */
#define EDGESTRING_SERIALNUM 3 /* Serial Number */
#define EDGESTRING_ASSEMNUM 4 /* Assembly Number */
#define EDGESTRING_OEMASSEMNUM 5 /* OEM Assembly Number */
#define EDGESTRING_MANUFDATE 6 /* Manufacture Date */
#define EDGESTRING_ORIGSERIALNUM 7 /* Serial Number */
struct string_block {
__u16 NumStrings; /* Number of strings in block */
__u16 Strings[1]; /* Start of string block */
};
#endif

View file

@ -0,0 +1,455 @@
/************************************************************************
*
* IONSP.H Definitions for I/O Networks Serial Protocol
*
* Copyright (C) 1997-1998 Inside Out Networks, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* These definitions are used by both kernel-mode driver and the
* peripheral firmware and MUST be kept in sync.
*
************************************************************************/
/************************************************************************
The data to and from all ports on the peripheral is multiplexed
through a single endpoint pair (EP1 since it supports 64-byte
MaxPacketSize). Therefore, the data, commands, and status for
each port must be preceded by a short header identifying the
destination port. The header also identifies the bytes that follow
as data or as command/status info.
Header format, first byte:
CLLLLPPP
--------
| | |------ Port Number: 0-7
| |--------- Length: MSB bits of length
|----------- Data/Command: 0 = Data header
1 = Cmd / Status (Cmd if OUT, Status if IN)
This gives 2 possible formats:
Data header: 0LLLLPPP LLLLLLLL
============
Where (LLLL,LLLLLLL) is 12-bit length of data that follows for
port number (PPP). The length is 0-based (0-FFF means 0-4095
bytes). The ~4K limit allows the host driver (which deals in
transfer requests instead of individual packets) to write a
large chunk of data in a single request. Note, however, that
the length must always be <= the current TxCredits for a given
port due to buffering limitations on the peripheral.
Cmd/Status header: 1ccccPPP [ CCCCCCCC, Params ]...
==================
Where (cccc) or (cccc,CCCCCCCC) is the cmd or status identifier.
Frequently-used values are encoded as (cccc), longer ones using
(cccc,CCCCCCCC). Subsequent bytes are optional parameters and are
specific to the cmd or status code. This may include a length
for command and status codes that need variable-length parameters.
In addition, we use another interrupt pipe (endpoint) which the host polls
periodically for flow control information. The peripheral, when there has
been a change, sends the following 10-byte packet:
RRRRRRRRRRRRRRRR
T0T0T0T0T0T0T0T0
T1T1T1T1T1T1T1T1
T2T2T2T2T2T2T2T2
T3T3T3T3T3T3T3T3
The first field is the 16-bit RxBytesAvail field, which indicates the
number of bytes which may be read by the host from EP1. This is necessary:
(a) because OSR2.1 has a bug which causes data loss if the peripheral returns
fewer bytes than the host expects to read, and (b) because, on Microsoft
platforms at least, an outstanding read posted on EP1 consumes about 35% of
the CPU just polling the device for data.
The next 4 fields are the 16-bit TxCredits for each port, which indicate how
many bytes the host is allowed to send on EP1 for transmit to a given port.
After an OPEN_PORT command, the Edgeport sends the initial TxCredits for that
port.
All 16-bit fields are sent in little-endian (Intel) format.
************************************************************************/
//
// Define format of InterruptStatus packet returned from the
// Interrupt pipe
//
struct int_status_pkt {
__u16 RxBytesAvail; // Additional bytes available to
// be read from Bulk IN pipe
__u16 TxCredits[MAX_RS232_PORTS]; // Additional space available in
// given port's TxBuffer
};
#define GET_INT_STATUS_SIZE(NumPorts) (sizeof(__u16) + (sizeof(__u16) * (NumPorts)))
//
// Define cmd/status header values and macros to extract them.
//
// Data: 0LLLLPPP LLLLLLLL
// Cmd/Stat: 1ccccPPP CCCCCCCC
#define IOSP_DATA_HDR_SIZE 2
#define IOSP_CMD_HDR_SIZE 2
#define IOSP_MAX_DATA_LENGTH 0x0FFF // 12 bits -> 4K
#define IOSP_PORT_MASK 0x07 // Mask to isolate port number
#define IOSP_CMD_STAT_BIT 0x80 // If set, this is command/status header
#define IS_CMD_STAT_HDR(Byte1) ((Byte1) & IOSP_CMD_STAT_BIT)
#define IS_DATA_HDR(Byte1) (!IS_CMD_STAT_HDR(Byte1))
#define IOSP_GET_HDR_PORT(Byte1) ((__u8) ((Byte1) & IOSP_PORT_MASK))
#define IOSP_GET_HDR_DATA_LEN(Byte1, Byte2) ((__u16) (((__u16)((Byte1) & 0x78)) << 5) | (Byte2))
#define IOSP_GET_STATUS_CODE(Byte1) ((__u8) (((Byte1) & 0x78) >> 3))
//
// These macros build the 1st and 2nd bytes for a data header
//
#define IOSP_BUILD_DATA_HDR1(Port, Len) ((__u8) (((Port) | ((__u8) (((__u16) (Len)) >> 5) & 0x78))))
#define IOSP_BUILD_DATA_HDR2(Port, Len) ((__u8) (Len))
//
// These macros build the 1st and 2nd bytes for a command header
//
#define IOSP_BUILD_CMD_HDR1(Port, Cmd) ((__u8) (IOSP_CMD_STAT_BIT | (Port) | ((__u8) ((Cmd) << 3))))
//--------------------------------------------------------------
//
// Define values for commands and command parameters
// (sent from Host to Edgeport)
//
// 1ccccPPP P1P1P1P1 [ P2P2P2P2P2 ]...
//
// cccc: 00-07 2-byte commands. Write UART register 0-7 with
// value in P1. See 16650.H for definitions of
// UART register numbers and contents.
//
// 08-0B 3-byte commands: ==== P1 ==== ==== P2 ====
// 08 available for expansion
// 09 1-param commands Command Code Param
// 0A available for expansion
// 0B available for expansion
//
// 0C-0D 4-byte commands. P1 = extended cmd and P2,P3 = params
// Currently unimplemented.
//
// 0E-0F N-byte commands: P1 = num bytes after P1 (ie, TotalLen - 2)
// P2 = extended cmd, P3..Pn = parameters.
// Currently unimplemented.
//
#define IOSP_WRITE_UART_REG(n) ((n) & 0x07) // UartReg[ n ] := P1
// Register numbers and contents
// defined in 16554.H.
// 0x08 // Available for expansion.
#define IOSP_EXT_CMD 0x09 // P1 = Command code (defined below)
// P2 = Parameter
//
// Extended Command values, used with IOSP_EXT_CMD, may
// or may not use parameter P2.
//
#define IOSP_CMD_OPEN_PORT 0x00 // Enable ints, init UART. (NO PARAM)
#define IOSP_CMD_CLOSE_PORT 0x01 // Disable ints, flush buffers. (NO PARAM)
#define IOSP_CMD_CHASE_PORT 0x02 // Wait for Edgeport TX buffers to empty. (NO PARAM)
#define IOSP_CMD_SET_RX_FLOW 0x03 // Set Rx Flow Control in Edgeport
#define IOSP_CMD_SET_TX_FLOW 0x04 // Set Tx Flow Control in Edgeport
#define IOSP_CMD_SET_XON_CHAR 0x05 // Set XON Character in Edgeport
#define IOSP_CMD_SET_XOFF_CHAR 0x06 // Set XOFF Character in Edgeport
#define IOSP_CMD_RX_CHECK_REQ 0x07 // Request Edgeport to insert a Checkpoint into
// the receive data stream (Parameter = 1 byte sequence number)
#define IOSP_CMD_SET_BREAK 0x08 // Turn on the BREAK (LCR bit 6)
#define IOSP_CMD_CLEAR_BREAK 0x09 // Turn off the BREAK (LCR bit 6)
//
// Define macros to simplify building of IOSP cmds
//
#define MAKE_CMD_WRITE_REG(ppBuf, pLen, Port, Reg, Val) \
do { \
(*(ppBuf))[0] = IOSP_BUILD_CMD_HDR1((Port), \
IOSP_WRITE_UART_REG(Reg)); \
(*(ppBuf))[1] = (Val); \
\
*ppBuf += 2; \
*pLen += 2; \
} while (0)
#define MAKE_CMD_EXT_CMD(ppBuf, pLen, Port, ExtCmd, Param) \
do { \
(*(ppBuf))[0] = IOSP_BUILD_CMD_HDR1((Port), IOSP_EXT_CMD); \
(*(ppBuf))[1] = (ExtCmd); \
(*(ppBuf))[2] = (Param); \
\
*ppBuf += 3; \
*pLen += 3; \
} while (0)
//--------------------------------------------------------------
//
// Define format of flow control commands
// (sent from Host to Edgeport)
//
// 11001PPP FlowCmd FlowTypes
//
// Note that the 'FlowTypes' parameter is a bit mask; that is,
// more than one flow control type can be active at the same time.
// FlowTypes = 0 means 'no flow control'.
//
//
// IOSP_CMD_SET_RX_FLOW
//
// Tells Edgeport how it can stop incoming UART data
//
// Example for Port 0
// P0 = 11001000
// P1 = IOSP_CMD_SET_RX_FLOW
// P2 = Bit mask as follows:
#define IOSP_RX_FLOW_RTS 0x01 // Edgeport drops RTS to stop incoming data
#define IOSP_RX_FLOW_DTR 0x02 // Edgeport drops DTR to stop incoming data
#define IOSP_RX_FLOW_DSR_SENSITIVITY 0x04 // Ignores Rx data unless DSR high
// Not currently implemented by firmware.
#define IOSP_RX_FLOW_XON_XOFF 0x08 // Edgeport sends XOFF char to stop incoming data.
// Host must have previously programmed the
// XON/XOFF values with SET_XON/SET_XOFF
// before enabling this bit.
//
// IOSP_CMD_SET_TX_FLOW
//
// Tells Edgeport what signal(s) will stop it from transmitting UART data
//
// Example for Port 0
// P0 = 11001000
// P1 = IOSP_CMD_SET_TX_FLOW
// P2 = Bit mask as follows:
#define IOSP_TX_FLOW_CTS 0x01 // Edgeport stops Tx if CTS low
#define IOSP_TX_FLOW_DSR 0x02 // Edgeport stops Tx if DSR low
#define IOSP_TX_FLOW_DCD 0x04 // Edgeport stops Tx if DCD low
#define IOSP_TX_FLOW_XON_XOFF 0x08 // Edgeport stops Tx upon receiving XOFF char.
// Host must have previously programmed the
// XON/XOFF values with SET_XON/SET_XOFF
// before enabling this bit.
#define IOSP_TX_FLOW_XOFF_CONTINUE 0x10 // If not set, Edgeport stops Tx when
// sending XOFF in order to fix broken
// systems that interpret the next
// received char as XON.
// If set, Edgeport continues Tx
// normally after transmitting XOFF.
// Not currently implemented by firmware.
#define IOSP_TX_TOGGLE_RTS 0x20 // Edgeport drives RTS as a true half-duplex
// Request-to-Send signal: it is raised before
// beginning transmission and lowered after
// the last Tx char leaves the UART.
// Not currently implemented by firmware.
//
// IOSP_CMD_SET_XON_CHAR
//
// Sets the character which Edgeport transmits/interprets as XON.
// Note: This command MUST be sent before sending a SET_RX_FLOW or
// SET_TX_FLOW with the XON_XOFF bit set.
//
// Example for Port 0
// P0 = 11001000
// P1 = IOSP_CMD_SET_XON_CHAR
// P2 = 0x11
//
// IOSP_CMD_SET_XOFF_CHAR
//
// Sets the character which Edgeport transmits/interprets as XOFF.
// Note: This command must be sent before sending a SET_RX_FLOW or
// SET_TX_FLOW with the XON_XOFF bit set.
//
// Example for Port 0
// P0 = 11001000
// P1 = IOSP_CMD_SET_XOFF_CHAR
// P2 = 0x13
//
// IOSP_CMD_RX_CHECK_REQ
//
// This command is used to assist in the implementation of the
// IOCTL_SERIAL_PURGE Windows IOCTL.
// This IOSP command tries to place a marker at the end of the RX
// queue in the Edgeport. If the Edgeport RX queue is full then
// the Check will be discarded.
// It is up to the device driver to timeout waiting for the
// RX_CHECK_RSP. If a RX_CHECK_RSP is received, the driver is
// sure that all data has been received from the edgeport and
// may now purge any internal RX buffers.
// Note tat the sequence numbers may be used to detect lost
// CHECK_REQs.
// Example for Port 0
// P0 = 11001000
// P1 = IOSP_CMD_RX_CHECK_REQ
// P2 = Sequence number
// Response will be:
// P1 = IOSP_EXT_RX_CHECK_RSP
// P2 = Request Sequence number
//--------------------------------------------------------------
//
// Define values for status and status parameters
// (received by Host from Edgeport)
//
// 1ssssPPP P1P1P1P1 [ P2P2P2P2P2 ]...
//
// ssss: 00-07 2-byte status. ssss identifies which UART register
// has changed value, and the new value is in P1.
// Note that the ssss values do not correspond to the
// 16554 register numbers given in 16554.H. Instead,
// see below for definitions of the ssss numbers
// used in this status message.
//
// 08-0B 3-byte status: ==== P1 ==== ==== P2 ====
// 08 LSR_DATA: New LSR Errored byte
// 09 1-param responses Response Code Param
// 0A OPEN_RSP: InitialMsr TxBufferSize
// 0B available for expansion
//
// 0C-0D 4-byte status. P1 = extended status code and P2,P3 = params
// Not currently implemented.
//
// 0E-0F N-byte status: P1 = num bytes after P1 (ie, TotalLen - 2)
// P2 = extended status, P3..Pn = parameters.
// Not currently implemented.
//
/****************************************************
* SSSS values for 2-byte status messages (0-8)
****************************************************/
#define IOSP_STATUS_LSR 0x00 // P1 is new value of LSR register.
// Bits defined in 16554.H. Edgeport
// returns this in order to report
// line status errors (overrun,
// parity, framing, break). This form
// is used when a errored receive data
// character was NOT present in the
// UART when the LSR error occurred
// (ie, when LSR bit 0 = 0).
#define IOSP_STATUS_MSR 0x01 // P1 is new value of MSR register.
// Bits defined in 16554.H. Edgeport
// returns this in order to report
// changes in modem status lines
// (CTS, DSR, RI, CD)
//
// 0x02 // Available for future expansion
// 0x03 //
// 0x04 //
// 0x05 //
// 0x06 //
// 0x07 //
/****************************************************
* SSSS values for 3-byte status messages (8-A)
****************************************************/
#define IOSP_STATUS_LSR_DATA 0x08 // P1 is new value of LSR register (same as STATUS_LSR)
// P2 is errored character read from
// RxFIFO after LSR reported an error.
#define IOSP_EXT_STATUS 0x09 // P1 is status/response code, param in P2.
// Response Codes (P1 values) for 3-byte status messages
#define IOSP_EXT_STATUS_CHASE_RSP 0 // Reply to CHASE_PORT cmd. P2 is outcome:
#define IOSP_EXT_STATUS_CHASE_PASS 0 // P2 = 0: All Tx data drained successfully
#define IOSP_EXT_STATUS_CHASE_FAIL 1 // P2 = 1: Timed out (stuck due to flow
// control from remote device).
#define IOSP_EXT_STATUS_RX_CHECK_RSP 1 // Reply to RX_CHECK cmd. P2 is sequence number
#define IOSP_STATUS_OPEN_RSP 0x0A // Reply to OPEN_PORT cmd.
// P1 is Initial MSR value
// P2 is encoded TxBuffer Size:
// TxBufferSize = (P2 + 1) * 64
// 0x0B // Available for future expansion
#define GET_TX_BUFFER_SIZE(P2) (((P2) + 1) * 64)
/****************************************************
* SSSS values for 4-byte status messages
****************************************************/
#define IOSP_EXT4_STATUS 0x0C // Extended status code in P1,
// Params in P2, P3
// Currently unimplemented.
// 0x0D // Currently unused, available.
//
// Macros to parse status messages
//
#define IOSP_GET_STATUS_LEN(code) ((code) < 8 ? 2 : ((code) < 0x0A ? 3 : 4))
#define IOSP_STATUS_IS_2BYTE(code) ((code) < 0x08)
#define IOSP_STATUS_IS_3BYTE(code) (((code) >= 0x08) && ((code) <= 0x0B))
#define IOSP_STATUS_IS_4BYTE(code) (((code) >= 0x0C) && ((code) <= 0x0D))

View file

@ -0,0 +1,232 @@
/*
* IO Edgeport Driver tables
*
* Copyright (C) 2001
* Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/
#ifndef IO_TABLES_H
#define IO_TABLES_H
static const struct usb_device_id edgeport_2port_id_table[] = {
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) },
{ }
};
static const struct usb_device_id edgeport_4port_id_table[] = {
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_RAPIDPORT_4) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4T) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
{ }
};
static const struct usb_device_id edgeport_8port_id_table[] = {
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
{ }
};
static const struct usb_device_id Epic_port_id_table[] = {
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
{ }
};
/* Devices that this driver supports */
static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_RAPIDPORT_4) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4T) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table_combined);
static struct usb_serial_driver edgeport_2port_device = {
.driver = {
.owner = THIS_MODULE,
.name = "edgeport_2",
},
.description = "Edgeport 2 port adapter",
.id_table = edgeport_2port_id_table,
.num_ports = 2,
.open = edge_open,
.close = edge_close,
.throttle = edge_throttle,
.unthrottle = edge_unthrottle,
.attach = edge_startup,
.disconnect = edge_disconnect,
.release = edge_release,
.port_probe = edge_port_probe,
.port_remove = edge_port_remove,
.ioctl = edge_ioctl,
.set_termios = edge_set_termios,
.tiocmget = edge_tiocmget,
.tiocmset = edge_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.write = edge_write,
.write_room = edge_write_room,
.chars_in_buffer = edge_chars_in_buffer,
.break_ctl = edge_break,
.read_int_callback = edge_interrupt_callback,
.read_bulk_callback = edge_bulk_in_callback,
.write_bulk_callback = edge_bulk_out_data_callback,
};
static struct usb_serial_driver edgeport_4port_device = {
.driver = {
.owner = THIS_MODULE,
.name = "edgeport_4",
},
.description = "Edgeport 4 port adapter",
.id_table = edgeport_4port_id_table,
.num_ports = 4,
.open = edge_open,
.close = edge_close,
.throttle = edge_throttle,
.unthrottle = edge_unthrottle,
.attach = edge_startup,
.disconnect = edge_disconnect,
.release = edge_release,
.port_probe = edge_port_probe,
.port_remove = edge_port_remove,
.ioctl = edge_ioctl,
.set_termios = edge_set_termios,
.tiocmget = edge_tiocmget,
.tiocmset = edge_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.write = edge_write,
.write_room = edge_write_room,
.chars_in_buffer = edge_chars_in_buffer,
.break_ctl = edge_break,
.read_int_callback = edge_interrupt_callback,
.read_bulk_callback = edge_bulk_in_callback,
.write_bulk_callback = edge_bulk_out_data_callback,
};
static struct usb_serial_driver edgeport_8port_device = {
.driver = {
.owner = THIS_MODULE,
.name = "edgeport_8",
},
.description = "Edgeport 8 port adapter",
.id_table = edgeport_8port_id_table,
.num_ports = 8,
.open = edge_open,
.close = edge_close,
.throttle = edge_throttle,
.unthrottle = edge_unthrottle,
.attach = edge_startup,
.disconnect = edge_disconnect,
.release = edge_release,
.port_probe = edge_port_probe,
.port_remove = edge_port_remove,
.ioctl = edge_ioctl,
.set_termios = edge_set_termios,
.tiocmget = edge_tiocmget,
.tiocmset = edge_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.write = edge_write,
.write_room = edge_write_room,
.chars_in_buffer = edge_chars_in_buffer,
.break_ctl = edge_break,
.read_int_callback = edge_interrupt_callback,
.read_bulk_callback = edge_bulk_in_callback,
.write_bulk_callback = edge_bulk_out_data_callback,
};
static struct usb_serial_driver epic_device = {
.driver = {
.owner = THIS_MODULE,
.name = "epic",
},
.description = "EPiC device",
.id_table = Epic_port_id_table,
.num_ports = 1,
.open = edge_open,
.close = edge_close,
.throttle = edge_throttle,
.unthrottle = edge_unthrottle,
.attach = edge_startup,
.disconnect = edge_disconnect,
.release = edge_release,
.port_probe = edge_port_probe,
.port_remove = edge_port_remove,
.ioctl = edge_ioctl,
.set_termios = edge_set_termios,
.tiocmget = edge_tiocmget,
.tiocmset = edge_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.write = edge_write,
.write_room = edge_write_room,
.chars_in_buffer = edge_chars_in_buffer,
.break_ctl = edge_break,
.read_int_callback = edge_interrupt_callback,
.read_bulk_callback = edge_bulk_in_callback,
.write_bulk_callback = edge_bulk_out_data_callback,
};
static struct usb_serial_driver * const serial_drivers[] = {
&edgeport_2port_device, &edgeport_4port_device,
&edgeport_8port_device, &epic_device, NULL
};
#endif

2595
drivers/usb/serial/io_ti.c Normal file

File diff suppressed because it is too large Load diff

186
drivers/usb/serial/io_ti.h Normal file
View file

@ -0,0 +1,186 @@
/*****************************************************************************
*
* Copyright (C) 1997-2002 Inside Out Networks, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*
* Feb-16-2001 DMI Added I2C structure definitions
* May-29-2002 gkh Ported to Linux
*
*
******************************************************************************/
#ifndef _IO_TI_H_
#define _IO_TI_H_
/* Address Space */
#define DTK_ADDR_SPACE_XDATA 0x03 /* Addr is placed in XDATA space */
#define DTK_ADDR_SPACE_I2C_TYPE_II 0x82 /* Addr is placed in I2C area */
#define DTK_ADDR_SPACE_I2C_TYPE_III 0x83 /* Addr is placed in I2C area */
/* UART Defines */
#define UMPMEM_BASE_UART1 0xFFA0 /* UMP UART1 base address */
#define UMPMEM_BASE_UART2 0xFFB0 /* UMP UART2 base address */
#define UMPMEM_OFFS_UART_LSR 0x05 /* UMP UART LSR register offset */
/* Bits per character */
#define UMP_UART_CHAR5BITS 0x00
#define UMP_UART_CHAR6BITS 0x01
#define UMP_UART_CHAR7BITS 0x02
#define UMP_UART_CHAR8BITS 0x03
/* Parity */
#define UMP_UART_NOPARITY 0x00
#define UMP_UART_ODDPARITY 0x01
#define UMP_UART_EVENPARITY 0x02
#define UMP_UART_MARKPARITY 0x03
#define UMP_UART_SPACEPARITY 0x04
/* Stop bits */
#define UMP_UART_STOPBIT1 0x00
#define UMP_UART_STOPBIT15 0x01
#define UMP_UART_STOPBIT2 0x02
/* Line status register masks */
#define UMP_UART_LSR_OV_MASK 0x01
#define UMP_UART_LSR_PE_MASK 0x02
#define UMP_UART_LSR_FE_MASK 0x04
#define UMP_UART_LSR_BR_MASK 0x08
#define UMP_UART_LSR_ER_MASK 0x0F
#define UMP_UART_LSR_RX_MASK 0x10
#define UMP_UART_LSR_TX_MASK 0x20
#define UMP_UART_LSR_DATA_MASK (LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK)
/* Port Settings Constants) */
#define UMP_MASK_UART_FLAGS_RTS_FLOW 0x0001
#define UMP_MASK_UART_FLAGS_RTS_DISABLE 0x0002
#define UMP_MASK_UART_FLAGS_PARITY 0x0008
#define UMP_MASK_UART_FLAGS_OUT_X_DSR_FLOW 0x0010
#define UMP_MASK_UART_FLAGS_OUT_X_CTS_FLOW 0x0020
#define UMP_MASK_UART_FLAGS_OUT_X 0x0040
#define UMP_MASK_UART_FLAGS_OUT_XA 0x0080
#define UMP_MASK_UART_FLAGS_IN_X 0x0100
#define UMP_MASK_UART_FLAGS_DTR_FLOW 0x0800
#define UMP_MASK_UART_FLAGS_DTR_DISABLE 0x1000
#define UMP_MASK_UART_FLAGS_RECEIVE_MS_INT 0x2000
#define UMP_MASK_UART_FLAGS_AUTO_START_ON_ERR 0x4000
#define UMP_DMA_MODE_CONTINOUS 0x01
#define UMP_PIPE_TRANS_TIMEOUT_ENA 0x80
#define UMP_PIPE_TRANSFER_MODE_MASK 0x03
#define UMP_PIPE_TRANS_TIMEOUT_MASK 0x7C
/* Purge port Direction Mask Bits */
#define UMP_PORT_DIR_OUT 0x01
#define UMP_PORT_DIR_IN 0x02
/* Address of Port 0 */
#define UMPM_UART1_PORT 0x03
/* Commands */
#define UMPC_SET_CONFIG 0x05
#define UMPC_OPEN_PORT 0x06
#define UMPC_CLOSE_PORT 0x07
#define UMPC_START_PORT 0x08
#define UMPC_STOP_PORT 0x09
#define UMPC_TEST_PORT 0x0A
#define UMPC_PURGE_PORT 0x0B
/* Force the Firmware to complete the current Read */
#define UMPC_COMPLETE_READ 0x80
/* Force UMP back into BOOT Mode */
#define UMPC_HARDWARE_RESET 0x81
/*
* Copy current download image to type 0xf2 record in 16k I2C
* firmware will change 0xff record to type 2 record when complete
*/
#define UMPC_COPY_DNLD_TO_I2C 0x82
/*
* Special function register commands
* wIndex is register address
* wValue is MSB/LSB mask/data
*/
#define UMPC_WRITE_SFR 0x83 /* Write SFR Register */
/* wIndex is register address */
#define UMPC_READ_SFR 0x84 /* Read SRF Register */
/* Set or Clear DTR (wValue bit 0 Set/Clear) wIndex ModuleID (port) */
#define UMPC_SET_CLR_DTR 0x85
/* Set or Clear RTS (wValue bit 0 Set/Clear) wIndex ModuleID (port) */
#define UMPC_SET_CLR_RTS 0x86
/* Set or Clear LOOPBACK (wValue bit 0 Set/Clear) wIndex ModuleID (port) */
#define UMPC_SET_CLR_LOOPBACK 0x87
/* Set or Clear BREAK (wValue bit 0 Set/Clear) wIndex ModuleID (port) */
#define UMPC_SET_CLR_BREAK 0x88
/* Read MSR wIndex ModuleID (port) */
#define UMPC_READ_MSR 0x89
/* Toolkit commands */
/* Read-write group */
#define UMPC_MEMORY_READ 0x92
#define UMPC_MEMORY_WRITE 0x93
/*
* UMP DMA Definitions
*/
#define UMPD_OEDB1_ADDRESS 0xFF08
#define UMPD_OEDB2_ADDRESS 0xFF10
struct out_endpoint_desc_block {
__u8 Configuration;
__u8 XBufAddr;
__u8 XByteCount;
__u8 Unused1;
__u8 Unused2;
__u8 YBufAddr;
__u8 YByteCount;
__u8 BufferSize;
} __attribute__((packed));
/*
* TYPE DEFINITIONS
* Structures for Firmware commands
*/
/* UART settings */
struct ump_uart_config {
__u16 wBaudRate; /* Baud rate */
__u16 wFlags; /* Bitmap mask of flags */
__u8 bDataBits; /* 5..8 - data bits per character */
__u8 bParity; /* Parity settings */
__u8 bStopBits; /* Stop bits settings */
char cXon; /* XON character */
char cXoff; /* XOFF character */
__u8 bUartMode; /* Will be updated when a user */
/* interface is defined */
} __attribute__((packed));
/*
* TYPE DEFINITIONS
* Structures for USB interrupts
*/
/* Interrupt packet structure */
struct ump_interrupt {
__u8 bICode; /* Interrupt code (interrupt num) */
__u8 bIInfo; /* Interrupt information */
} __attribute__((packed));
#define TIUMP_GET_PORT_FROM_CODE(c) (((c) >> 4) - 3)
#define TIUMP_GET_FUNC_FROM_CODE(c) ((c) & 0x0f)
#define TIUMP_INTERRUPT_CODE_LSR 0x03
#define TIUMP_INTERRUPT_CODE_MSR 0x04
#endif

View file

@ -0,0 +1,683 @@
/************************************************************************
*
* USBVEND.H Vendor-specific USB definitions
*
* NOTE: This must be kept in sync with the Edgeport firmware and
* must be kept backward-compatible with older firmware.
*
************************************************************************
*
* Copyright (C) 1998 Inside Out Networks, Inc.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
************************************************************************/
#if !defined(_USBVEND_H)
#define _USBVEND_H
/************************************************************************
*
* D e f i n e s / T y p e d e f s
*
************************************************************************/
//
// Definitions of USB product IDs
//
#define USB_VENDOR_ID_ION 0x1608 // Our VID
#define USB_VENDOR_ID_TI 0x0451 // TI VID
#define USB_VENDOR_ID_AXIOHM 0x05D9 /* Axiohm VID */
//
// Definitions of USB product IDs (PID)
// We break the USB-defined PID into an OEM Id field (upper 6 bits)
// and a Device Id (bottom 10 bits). The Device Id defines what
// device this actually is regardless of what the OEM wants to
// call it.
//
// ION-device OEM IDs
#define ION_OEM_ID_ION 0 // 00h Inside Out Networks
#define ION_OEM_ID_NLYNX 1 // 01h NLynx Systems
#define ION_OEM_ID_GENERIC 2 // 02h Generic OEM
#define ION_OEM_ID_MAC 3 // 03h Mac Version
#define ION_OEM_ID_MEGAWOLF 4 // 04h Lupusb OEM Mac version (MegaWolf)
#define ION_OEM_ID_MULTITECH 5 // 05h Multitech Rapidports
#define ION_OEM_ID_AGILENT 6 // 06h AGILENT board
// ION-device Device IDs
// Product IDs - assigned to match middle digit of serial number (No longer true)
#define ION_DEVICE_ID_80251_NETCHIP 0x020 // This bit is set in the PID if this edgeport hardware$
// is based on the 80251+Netchip.
#define ION_DEVICE_ID_GENERATION_1 0x00 // Value for 930 based edgeports
#define ION_DEVICE_ID_GENERATION_2 0x01 // Value for 80251+Netchip.
#define ION_DEVICE_ID_GENERATION_3 0x02 // Value for Texas Instruments TUSB5052 chip
#define ION_DEVICE_ID_GENERATION_4 0x03 // Watchport Family of products
#define ION_GENERATION_MASK 0x03
#define ION_DEVICE_ID_HUB_MASK 0x0080 // This bit in the PID designates a HUB device
// for example 8C would be a 421 4 port hub
// and 8D would be a 2 port embedded hub
#define EDGEPORT_DEVICE_ID_MASK 0x0ff // Not including OEM or GENERATION fields
#define ION_DEVICE_ID_UNCONFIGURED_EDGE_DEVICE 0x000 // In manufacturing only
#define ION_DEVICE_ID_EDGEPORT_4 0x001 // Edgeport/4 RS232
#define ION_DEVICE_ID_EDGEPORT_8R 0x002 // Edgeport with RJ45 no Ring
#define ION_DEVICE_ID_RAPIDPORT_4 0x003 // Rapidport/4
#define ION_DEVICE_ID_EDGEPORT_4T 0x004 // Edgeport/4 RS232 for Telxon (aka "Fleetport")
#define ION_DEVICE_ID_EDGEPORT_2 0x005 // Edgeport/2 RS232
#define ION_DEVICE_ID_EDGEPORT_4I 0x006 // Edgeport/4 RS422
#define ION_DEVICE_ID_EDGEPORT_2I 0x007 // Edgeport/2 RS422/RS485
#define ION_DEVICE_ID_EDGEPORT_8RR 0x008 // Edgeport with RJ45 with Data and RTS/CTS only
// ION_DEVICE_ID_EDGEPORT_8_HANDBUILT 0x009 // Hand-built Edgeport/8 (Placeholder, used in middle digit of serial number only!)
// ION_DEVICE_ID_MULTIMODEM_4X56 0x00A // MultiTech version of RP/4 (Placeholder, used in middle digit of serial number only!)
#define ION_DEVICE_ID_EDGEPORT_PARALLEL_PORT 0x00B // Edgeport/(4)21 Parallel port (USS720)
#define ION_DEVICE_ID_EDGEPORT_421 0x00C // Edgeport/421 Hub+RS232+Parallel
#define ION_DEVICE_ID_EDGEPORT_21 0x00D // Edgeport/21 RS232+Parallel
#define ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU 0x00E // Half of an Edgeport/8 (the kind with 2 EP/4s on 1 PCB)
#define ION_DEVICE_ID_EDGEPORT_8 0x00F // Edgeport/8 (single-CPU)
#define ION_DEVICE_ID_EDGEPORT_2_DIN 0x010 // Edgeport/2 RS232 with Apple DIN connector
#define ION_DEVICE_ID_EDGEPORT_4_DIN 0x011 // Edgeport/4 RS232 with Apple DIN connector
#define ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU 0x012 // Half of an Edgeport/16 (the kind with 2 EP/8s)
#define ION_DEVICE_ID_EDGEPORT_COMPATIBLE 0x013 // Edgeport Compatible, for NCR, Axiohm etc. testing
#define ION_DEVICE_ID_EDGEPORT_8I 0x014 // Edgeport/8 RS422 (single-CPU)
#define ION_DEVICE_ID_EDGEPORT_1 0x015 // Edgeport/1 RS232
#define ION_DEVICE_ID_EPOS44 0x016 // Half of an EPOS/44 (TIUMP BASED)
#define ION_DEVICE_ID_EDGEPORT_42 0x017 // Edgeport/42
#define ION_DEVICE_ID_EDGEPORT_412_8 0x018 // Edgeport/412 8 port part
#define ION_DEVICE_ID_EDGEPORT_412_4 0x019 // Edgeport/412 4 port part
#define ION_DEVICE_ID_EDGEPORT_22I 0x01A // Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232
// Compact Form factor TI based devices 2c, 21c, 22c, 221c
#define ION_DEVICE_ID_EDGEPORT_2C 0x01B // Edgeport/2c is a TI based Edgeport/2 - Small I2c
#define ION_DEVICE_ID_EDGEPORT_221C 0x01C // Edgeport/221c is a TI based Edgeport/2 with lucent chip and
// 2 external hub ports - Large I2C
#define ION_DEVICE_ID_EDGEPORT_22C 0x01D // Edgeport/22c is a TI based Edgeport/2 with
// 2 external hub ports - Large I2C
#define ION_DEVICE_ID_EDGEPORT_21C 0x01E // Edgeport/21c is a TI based Edgeport/2 with lucent chip
// Small I2C
/*
* DANGER DANGER The 0x20 bit was used to indicate a 8251/netchip GEN 2 device.
* Since the MAC, Linux, and Optimal drivers still used the old code
* I suggest that you skip the 0x20 bit when creating new PIDs
*/
// Generation 3 devices -- 3410 based edgport/1 (256 byte I2C)
#define ION_DEVICE_ID_TI3410_EDGEPORT_1 0x040 // Edgeport/1 RS232
#define ION_DEVICE_ID_TI3410_EDGEPORT_1I 0x041 // Edgeport/1i- RS422 model
// Ti based software switchable RS232/RS422/RS485 devices
#define ION_DEVICE_ID_EDGEPORT_4S 0x042 // Edgeport/4s - software switchable model
#define ION_DEVICE_ID_EDGEPORT_8S 0x043 // Edgeport/8s - software switchable model
// Usb to Ethernet dongle
#define ION_DEVICE_ID_EDGEPORT_E 0x0E0 // Edgeport/E Usb to Ethernet
// Edgeport TI based devices
#define ION_DEVICE_ID_TI_EDGEPORT_4 0x0201 // Edgeport/4 RS232
#define ION_DEVICE_ID_TI_EDGEPORT_2 0x0205 // Edgeport/2 RS232
#define ION_DEVICE_ID_TI_EDGEPORT_4I 0x0206 // Edgeport/4i RS422
#define ION_DEVICE_ID_TI_EDGEPORT_2I 0x0207 // Edgeport/2i RS422/RS485
#define ION_DEVICE_ID_TI_EDGEPORT_421 0x020C // Edgeport/421 4 hub 2 RS232 + Parallel (lucent on a different hub port)
#define ION_DEVICE_ID_TI_EDGEPORT_21 0x020D // Edgeport/21 2 RS232 + Parallel (lucent on a different hub port)
#define ION_DEVICE_ID_TI_EDGEPORT_416 0x0212 // Edgeport/416
#define ION_DEVICE_ID_TI_EDGEPORT_1 0x0215 // Edgeport/1 RS232
#define ION_DEVICE_ID_TI_EDGEPORT_42 0x0217 // Edgeport/42 4 hub 2 RS232
#define ION_DEVICE_ID_TI_EDGEPORT_22I 0x021A // Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232
#define ION_DEVICE_ID_TI_EDGEPORT_2C 0x021B // Edgeport/2c RS232
#define ION_DEVICE_ID_TI_EDGEPORT_221C 0x021C // Edgeport/221c is a TI based Edgeport/2 with lucent chip and
// 2 external hub ports - Large I2C
#define ION_DEVICE_ID_TI_EDGEPORT_22C 0x021D // Edgeport/22c is a TI based Edgeport/2 with
// 2 external hub ports - Large I2C
#define ION_DEVICE_ID_TI_EDGEPORT_21C 0x021E // Edgeport/21c is a TI based Edgeport/2 with lucent chip
// Generation 3 devices -- 3410 based edgport/1 (256 byte I2C)
#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1 0x0240 // Edgeport/1 RS232
#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I 0x0241 // Edgeport/1i- RS422 model
// Ti based software switchable RS232/RS422/RS485 devices
#define ION_DEVICE_ID_TI_EDGEPORT_4S 0x0242 // Edgeport/4s - software switchable model
#define ION_DEVICE_ID_TI_EDGEPORT_8S 0x0243 // Edgeport/8s - software switchable model
#define ION_DEVICE_ID_TI_EDGEPORT_8 0x0244 // Edgeport/8 (single-CPU)
#define ION_DEVICE_ID_TI_EDGEPORT_416B 0x0247 // Edgeport/416
/************************************************************************
*
* Generation 4 devices
*
************************************************************************/
// Watchport based on 3410 both 1-wire and binary products (16K I2C)
#define ION_DEVICE_ID_WP_UNSERIALIZED 0x300 // Watchport based on 3410 both 1-wire and binary products
#define ION_DEVICE_ID_WP_PROXIMITY 0x301 // Watchport/P Discontinued
#define ION_DEVICE_ID_WP_MOTION 0x302 // Watchport/M
#define ION_DEVICE_ID_WP_MOISTURE 0x303 // Watchport/W
#define ION_DEVICE_ID_WP_TEMPERATURE 0x304 // Watchport/T
#define ION_DEVICE_ID_WP_HUMIDITY 0x305 // Watchport/H
#define ION_DEVICE_ID_WP_POWER 0x306 // Watchport
#define ION_DEVICE_ID_WP_LIGHT 0x307 // Watchport
#define ION_DEVICE_ID_WP_RADIATION 0x308 // Watchport
#define ION_DEVICE_ID_WP_ACCELERATION 0x309 // Watchport/A
#define ION_DEVICE_ID_WP_DISTANCE 0x30A // Watchport/D Discontinued
#define ION_DEVICE_ID_WP_PROX_DIST 0x30B // Watchport/D uses distance sensor
// Default to /P function
#define ION_DEVICE_ID_PLUS_PWR_HP4CD 0x30C // 5052 Plus Power HubPort/4CD+ (for Dell)
#define ION_DEVICE_ID_PLUS_PWR_HP4C 0x30D // 5052 Plus Power HubPort/4C+
#define ION_DEVICE_ID_PLUS_PWR_PCI 0x30E // 3410 Plus Power PCI Host Controller 4 port
//
// Definitions for AXIOHM USB product IDs
//
#define USB_VENDOR_ID_AXIOHM 0x05D9 // Axiohm VID
#define AXIOHM_DEVICE_ID_MASK 0xffff
#define AXIOHM_DEVICE_ID_EPIC_A758 0xA758
#define AXIOHM_DEVICE_ID_EPIC_A794 0xA794
#define AXIOHM_DEVICE_ID_EPIC_A225 0xA225
//
// Definitions for NCR USB product IDs
//
#define USB_VENDOR_ID_NCR 0x0404 // NCR VID
#define NCR_DEVICE_ID_MASK 0xffff
#define NCR_DEVICE_ID_EPIC_0202 0x0202
#define NCR_DEVICE_ID_EPIC_0203 0x0203
#define NCR_DEVICE_ID_EPIC_0310 0x0310
#define NCR_DEVICE_ID_EPIC_0311 0x0311
#define NCR_DEVICE_ID_EPIC_0312 0x0312
//
// Definitions for SYMBOL USB product IDs
//
#define USB_VENDOR_ID_SYMBOL 0x05E0 // Symbol VID
#define SYMBOL_DEVICE_ID_MASK 0xffff
#define SYMBOL_DEVICE_ID_KEYFOB 0x0700
//
// Definitions for other product IDs
#define ION_DEVICE_ID_MT4X56USB 0x1403 // OEM device
#define GENERATION_ID_FROM_USB_PRODUCT_ID(ProductId) \
((__u16) ((ProductId >> 8) & (ION_GENERATION_MASK)))
#define MAKE_USB_PRODUCT_ID(OemId, DeviceId) \
((__u16) (((OemId) << 10) || (DeviceId)))
#define DEVICE_ID_FROM_USB_PRODUCT_ID(ProductId) \
((__u16) ((ProductId) & (EDGEPORT_DEVICE_ID_MASK)))
#define OEM_ID_FROM_USB_PRODUCT_ID(ProductId) \
((__u16) (((ProductId) >> 10) & 0x3F))
//
// Definitions of parameters for download code. Note that these are
// specific to a given version of download code and must change if the
// corresponding download code changes.
//
// TxCredits value below which driver won't bother sending (to prevent too many small writes).
// Send only if above 25%
#define EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(InitialCredit, MaxPacketSize) (max(((InitialCredit) / 4), (MaxPacketSize)))
#define EDGE_FW_BULK_MAX_PACKET_SIZE 64 // Max Packet Size for Bulk In Endpoint (EP1)
#define EDGE_FW_BULK_READ_BUFFER_SIZE 1024 // Size to use for Bulk reads
#define EDGE_FW_INT_MAX_PACKET_SIZE 32 // Max Packet Size for Interrupt In Endpoint
// Note that many units were shipped with MPS=16, we
// force an upgrade to this value).
#define EDGE_FW_INT_INTERVAL 2 // 2ms polling on IntPipe
//
// Definitions of I/O Networks vendor-specific requests
// for default endpoint
//
// bmRequestType = 01000000 Set vendor-specific, to device
// bmRequestType = 11000000 Get vendor-specific, to device
//
// These are the definitions for the bRequest field for the
// above bmRequestTypes.
//
// For the read/write Edgeport memory commands, the parameters
// are as follows:
// wValue = 16-bit address
// wIndex = unused (though we could put segment 00: or FF: here)
// wLength = # bytes to read/write (max 64)
//
#define USB_REQUEST_ION_RESET_DEVICE 0 // Warm reboot Edgeport, retaining USB address
#define USB_REQUEST_ION_GET_EPIC_DESC 1 // Get Edgeport Compatibility Descriptor
// unused 2 // Unused, available
#define USB_REQUEST_ION_READ_RAM 3 // Read EdgePort RAM at specified addr
#define USB_REQUEST_ION_WRITE_RAM 4 // Write EdgePort RAM at specified addr
#define USB_REQUEST_ION_READ_ROM 5 // Read EdgePort ROM at specified addr
#define USB_REQUEST_ION_WRITE_ROM 6 // Write EdgePort ROM at specified addr
#define USB_REQUEST_ION_EXEC_DL_CODE 7 // Begin execution of RAM-based download
// code by jumping to address in wIndex:wValue
// 8 // Unused, available
#define USB_REQUEST_ION_ENABLE_SUSPEND 9 // Enable/Disable suspend feature
// (wValue != 0: Enable; wValue = 0: Disable)
#define USB_REQUEST_ION_SEND_IOSP 10 // Send an IOSP command to the edgeport over the control pipe
#define USB_REQUEST_ION_RECV_IOSP 11 // Receive an IOSP command from the edgeport over the control pipe
#define USB_REQUEST_ION_DIS_INT_TIMER 0x80 // Sent to Axiohm to enable/ disable
// interrupt token timer
// wValue = 1, enable (default)
// wValue = 0, disable
//
// Define parameter values for our vendor-specific commands
//
//
// Edgeport Compatibility Descriptor
//
// This descriptor is only returned by Edgeport-compatible devices
// supporting the EPiC spec. True ION devices do not return this
// descriptor, but instead return STALL on receipt of the
// GET_EPIC_DESC command. The driver interprets a STALL to mean that
// this is a "real" Edgeport.
//
struct edge_compatibility_bits {
// This __u32 defines which Vendor-specific commands/functionality
// the device supports on the default EP0 pipe.
__u32 VendEnableSuspend : 1; // 0001 Set if device supports ION_ENABLE_SUSPEND
__u32 VendUnused : 31; // Available for future expansion, must be 0
// This __u32 defines which IOSP commands are supported over the
// bulk pipe EP1.
// xxxx Set if device supports:
__u32 IOSPOpen : 1; // 0001 OPEN / OPEN_RSP (Currently must be 1)
__u32 IOSPClose : 1; // 0002 CLOSE
__u32 IOSPChase : 1; // 0004 CHASE / CHASE_RSP
__u32 IOSPSetRxFlow : 1; // 0008 SET_RX_FLOW
__u32 IOSPSetTxFlow : 1; // 0010 SET_TX_FLOW
__u32 IOSPSetXChar : 1; // 0020 SET_XON_CHAR/SET_XOFF_CHAR
__u32 IOSPRxCheck : 1; // 0040 RX_CHECK_REQ/RX_CHECK_RSP
__u32 IOSPSetClrBreak : 1; // 0080 SET_BREAK/CLEAR_BREAK
__u32 IOSPWriteMCR : 1; // 0100 MCR register writes (set/clr DTR/RTS)
__u32 IOSPWriteLCR : 1; // 0200 LCR register writes (wordlen/stop/parity)
__u32 IOSPSetBaudRate : 1; // 0400 setting Baud rate (writes to LCR.80h and DLL/DLM register)
__u32 IOSPDisableIntPipe : 1; // 0800 Do not use the interrupt pipe for TxCredits or RxButesAvailable
__u32 IOSPRxDataAvail : 1; // 1000 Return status of RX Fifo (Data available in Fifo)
__u32 IOSPTxPurge : 1; // 2000 Purge TXBuffer and/or Fifo in Edgeport hardware
__u32 IOSPUnused : 18; // Available for future expansion, must be 0
// This __u32 defines which 'general' features are supported
__u32 TrueEdgeport : 1; // 0001 Set if device is a 'real' Edgeport
// (Used only by driver, NEVER set by an EPiC device)
__u32 GenUnused : 31; // Available for future expansion, must be 0
};
#define EDGE_COMPATIBILITY_MASK0 0x0001
#define EDGE_COMPATIBILITY_MASK1 0x3FFF
#define EDGE_COMPATIBILITY_MASK2 0x0001
struct edge_compatibility_descriptor {
__u8 Length; // Descriptor Length (per USB spec)
__u8 DescType; // Descriptor Type (per USB spec, =DEVICE type)
__u8 EpicVer; // Version of EPiC spec supported
// (Currently must be 1)
__u8 NumPorts; // Number of serial ports supported
__u8 iDownloadFile; // Index of string containing download code filename
// 0=no download, FF=download compiled into driver.
__u8 Unused[3]; // Available for future expansion, must be 0
// (Currently must be 0).
__u8 MajorVersion; // Firmware version: xx.
__u8 MinorVersion; // yy.
__le16 BuildNumber; // zzzz (LE format)
// The following structure contains __u32s, with each bit
// specifying whether the EPiC device supports the given
// command or functionality.
struct edge_compatibility_bits Supports;
};
// Values for iDownloadFile
#define EDGE_DOWNLOAD_FILE_NONE 0 // No download requested
#define EDGE_DOWNLOAD_FILE_INTERNAL 0xFF // Download the file compiled into driver (930 version)
#define EDGE_DOWNLOAD_FILE_I930 0xFF // Download the file compiled into driver (930 version)
#define EDGE_DOWNLOAD_FILE_80251 0xFE // Download the file compiled into driver (80251 version)
/*
* Special addresses for READ/WRITE_RAM/ROM
*/
// Version 1 (original) format of DeviceParams
#define EDGE_MANUF_DESC_ADDR_V1 0x00FF7F00
#define EDGE_MANUF_DESC_LEN_V1 sizeof(EDGE_MANUF_DESCRIPTOR_V1)
// Version 2 format of DeviceParams. This format is longer (3C0h)
// and starts lower in memory, at the uppermost 1K in ROM.
#define EDGE_MANUF_DESC_ADDR 0x00FF7C00
#define EDGE_MANUF_DESC_LEN sizeof(struct edge_manuf_descriptor)
// Boot params descriptor
#define EDGE_BOOT_DESC_ADDR 0x00FF7FC0
#define EDGE_BOOT_DESC_LEN sizeof(struct edge_boot_descriptor)
// Define the max block size that may be read or written
// in a read/write RAM/ROM command.
#define MAX_SIZE_REQ_ION_READ_MEM ((__u16)64)
#define MAX_SIZE_REQ_ION_WRITE_MEM ((__u16)64)
//
// Notes for the following two ION vendor-specific param descriptors:
//
// 1. These have a standard USB descriptor header so they look like a
// normal descriptor.
// 2. Any strings in the structures are in USB-defined string
// descriptor format, so that they may be separately retrieved,
// if necessary, with a minimum of work on the 930. This also
// requires them to be in UNICODE format, which, for English at
// least, simply means extending each __u8 into a __u16.
// 3. For all fields, 00 means 'uninitialized'.
// 4. All unused areas should be set to 00 for future expansion.
//
// This structure is ver 2 format. It contains ALL USB descriptors as
// well as the configuration parameters that were in the original V1
// structure. It is NOT modified when new boot code is downloaded; rather,
// these values are set or modified by manufacturing. It is located at
// xC00-xFBF (length 3C0h) in the ROM.
// This structure is a superset of the v1 structure and is arranged so
// that all of the v1 fields remain at the same address. We are just
// adding more room to the front of the structure to hold the descriptors.
//
// The actual contents of this structure are defined in a 930 assembly
// file, converted to a binary image, and then written by the serialization
// program. The C definition of this structure just defines a dummy
// area for general USB descriptors and the descriptor tables (the root
// descriptor starts at xC00). At the bottom of the structure are the
// fields inherited from the v1 structure.
#define MAX_SERIALNUMBER_LEN 12
#define MAX_ASSEMBLYNUMBER_LEN 14
struct edge_manuf_descriptor {
__u16 RootDescTable[0x10]; // C00 Root of descriptor tables (just a placeholder)
__u8 DescriptorArea[0x2E0]; // C20 Descriptors go here, up to 2E0h (just a placeholder)
// Start of v1-compatible section
__u8 Length; // F00 Desc length for what follows, per USB (= C0h )
__u8 DescType; // F01 Desc type, per USB (=DEVICE type)
__u8 DescVer; // F02 Desc version/format (currently 2)
__u8 NumRootDescEntries; // F03 # entries in RootDescTable
__u8 RomSize; // F04 Size of ROM/E2PROM in K
__u8 RamSize; // F05 Size of external RAM in K
__u8 CpuRev; // F06 CPU revision level (chg only if s/w visible)
__u8 BoardRev; // F07 PCB revision level (chg only if s/w visible)
__u8 NumPorts; // F08 Number of ports
__u8 DescDate[3]; // F09 MM/DD/YY when descriptor template was compiler,
// so host can track changes to USB-only descriptors.
__u8 SerNumLength; // F0C USB string descriptor len
__u8 SerNumDescType; // F0D USB descriptor type (=STRING type)
__le16 SerialNumber[MAX_SERIALNUMBER_LEN]; // F0E "01-01-000100" Unicode Serial Number
__u8 AssemblyNumLength; // F26 USB string descriptor len
__u8 AssemblyNumDescType; // F27 USB descriptor type (=STRING type)
__le16 AssemblyNumber[MAX_ASSEMBLYNUMBER_LEN]; // F28 "350-1000-01-A " assembly number
__u8 OemAssyNumLength; // F44 USB string descriptor len
__u8 OemAssyNumDescType; // F45 USB descriptor type (=STRING type)
__le16 OemAssyNumber[MAX_ASSEMBLYNUMBER_LEN]; // F46 "xxxxxxxxxxxxxx" OEM assembly number
__u8 ManufDateLength; // F62 USB string descriptor len
__u8 ManufDateDescType; // F63 USB descriptor type (=STRING type)
__le16 ManufDate[6]; // F64 "MMDDYY" manufacturing date
__u8 Reserved3[0x4D]; // F70 -- unused, set to 0 --
__u8 UartType; // FBD Uart Type
__u8 IonPid; // FBE Product ID, == LSB of USB DevDesc.PID
// (Note: Edgeport/4s before 11/98 will have
// 00 here instead of 01)
__u8 IonConfig; // FBF Config byte for ION manufacturing use
// FBF end of structure, total len = 3C0h
};
#define MANUF_DESC_VER_1 1 // Original definition of MANUF_DESC
#define MANUF_DESC_VER_2 2 // Ver 2, starts at xC00h len 3C0h
// Uart Types
// Note: Since this field was added only recently, all Edgeport/4 units
// shipped before 11/98 will have 00 in this field. Therefore,
// both 00 and 01 values mean '654.
#define MANUF_UART_EXAR_654_EARLY 0 // Exar 16C654 in Edgeport/4s before 11/98
#define MANUF_UART_EXAR_654 1 // Exar 16C654
#define MANUF_UART_EXAR_2852 2 // Exar 16C2852
//
// Note: The CpuRev and BoardRev values do not conform to manufacturing
// revisions; they are to be incremented only when the CPU or hardware
// changes in a software-visible way, such that the 930 software or
// the host driver needs to handle the hardware differently.
//
// Values of bottom 5 bits of CpuRev & BoardRev for
// Implementation 0 (ie, 930-based)
#define MANUF_CPU_REV_AD4 1 // 930 AD4, with EP1 Rx bug (needs RXSPM)
#define MANUF_CPU_REV_AD5 2 // 930 AD5, with above bug (supposedly) fixed
#define MANUF_CPU_80251 0x20 // Intel 80251
#define MANUF_BOARD_REV_A 1 // Original version, == Manuf Rev A
#define MANUF_BOARD_REV_B 2 // Manuf Rev B, wakeup interrupt works
#define MANUF_BOARD_REV_C 3 // Manuf Rev C, 2/4 ports, rs232/rs422
#define MANUF_BOARD_REV_GENERATION_2 0x20 // Second generaiton edgeport
// Values of bottom 5 bits of CpuRev & BoardRev for
// Implementation 1 (ie, 251+Netchip-based)
#define MANUF_CPU_REV_1 1 // C251TB Rev 1 (Need actual Intel rev here)
#define MANUF_BOARD_REV_A 1 // First rev of 251+Netchip design
#define MANUF_SERNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->SerialNumber)
#define MANUF_ASSYNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->AssemblyNumber)
#define MANUF_OEMASSYNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->OemAssyNumber)
#define MANUF_MANUFDATE_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->ManufDate)
#define MANUF_ION_CONFIG_DIAG_NO_LOOP 0x20 // As below but no ext loopback test
#define MANUF_ION_CONFIG_DIAG 0x40 // 930 based device: 1=Run h/w diags, 0=norm
// TIUMP Device : 1=IONSERIAL needs to run Final Test
#define MANUF_ION_CONFIG_MASTER 0x80 // 930 based device: 1=Master mode, 0=Normal
// TIUMP Device : 1=First device on a multi TIUMP Device
//
// This structure describes parameters for the boot code, and
// is programmed along with new boot code. These are values
// which are specific to a given build of the boot code. It
// is exactly 64 bytes long and is fixed at address FF:xFC0
// - FF:xFFF. Note that the 930-mandated UCONFIG bytes are
// included in this structure.
//
struct edge_boot_descriptor {
__u8 Length; // C0 Desc length, per USB (= 40h)
__u8 DescType; // C1 Desc type, per USB (= DEVICE type)
__u8 DescVer; // C2 Desc version/format
__u8 Reserved1; // C3 -- unused, set to 0 --
__le16 BootCodeLength; // C4 Boot code goes from FF:0000 to FF:(len-1)
// (LE format)
__u8 MajorVersion; // C6 Firmware version: xx.
__u8 MinorVersion; // C7 yy.
__le16 BuildNumber; // C8 zzzz (LE format)
__u16 EnumRootDescTable; // CA Root of ROM-based descriptor table
__u8 NumDescTypes; // CC Number of supported descriptor types
__u8 Reserved4; // CD Fix Compiler Packing
__le16 Capabilities; // CE-CF Capabilities flags (LE format)
__u8 Reserved2[0x28]; // D0 -- unused, set to 0 --
__u8 UConfig0; // F8 930-defined CPU configuration byte 0
__u8 UConfig1; // F9 930-defined CPU configuration byte 1
__u8 Reserved3[6]; // FA -- unused, set to 0 --
// FF end of structure, total len = 80
};
#define BOOT_DESC_VER_1 1 // Original definition of BOOT_PARAMS
#define BOOT_DESC_VER_2 2 // 2nd definition, descriptors not included in boot
// Capabilities flags
#define BOOT_CAP_RESET_CMD 0x0001 // If set, boot correctly supports ION_RESET_DEVICE
/************************************************************************
T I U M P D E F I N I T I O N S
***********************************************************************/
// Chip definitions in I2C
#define UMP5152 0x52
#define UMP3410 0x10
//************************************************************************
// TI I2C Format Definitions
//************************************************************************
#define I2C_DESC_TYPE_INFO_BASIC 0x01
#define I2C_DESC_TYPE_FIRMWARE_BASIC 0x02
#define I2C_DESC_TYPE_DEVICE 0x03
#define I2C_DESC_TYPE_CONFIG 0x04
#define I2C_DESC_TYPE_STRING 0x05
#define I2C_DESC_TYPE_FIRMWARE_AUTO 0x07 // for 3410 download
#define I2C_DESC_TYPE_CONFIG_KLUDGE 0x14 // for 3410
#define I2C_DESC_TYPE_WATCHPORT_VERSION 0x15 // firmware version number for watchport
#define I2C_DESC_TYPE_WATCHPORT_CALIBRATION_DATA 0x16 // Watchport Calibration Data
#define I2C_DESC_TYPE_FIRMWARE_BLANK 0xf2
// Special section defined by ION
#define I2C_DESC_TYPE_ION 0 // Not defined by TI
struct ti_i2c_desc {
__u8 Type; // Type of descriptor
__le16 Size; // Size of data only not including header
__u8 CheckSum; // Checksum (8 bit sum of data only)
__u8 Data[0]; // Data starts here
} __attribute__((packed));
// for 5152 devices only (type 2 record)
// for 3410 the version is stored in the WATCHPORT_FIRMWARE_VERSION descriptor
struct ti_i2c_firmware_rec {
__u8 Ver_Major; // Firmware Major version number
__u8 Ver_Minor; // Firmware Minor version number
__u8 Data[0]; // Download starts here
} __attribute__((packed));
struct watchport_firmware_version {
// Added 2 bytes for version number
__u8 Version_Major; // Download Version (for Watchport)
__u8 Version_Minor;
} __attribute__((packed));
// Structure of header of download image in fw_down.h
struct ti_i2c_image_header {
__le16 Length;
__u8 CheckSum;
} __attribute__((packed));
struct ti_basic_descriptor {
__u8 Power; // Self powered
// bit 7: 1 - power switching supported
// 0 - power switching not supported
//
// bit 0: 1 - self powered
// 0 - bus powered
//
//
__u16 HubVid; // VID HUB
__u16 HubPid; // PID HUB
__u16 DevPid; // PID Edgeport
__u8 HubTime; // Time for power on to power good
__u8 HubCurrent; // HUB Current = 100ma
} __attribute__((packed));
// CPU / Board Rev Definitions
#define TI_CPU_REV_5052 2 // 5052 based edgeports
#define TI_CPU_REV_3410 3 // 3410 based edgeports
#define TI_BOARD_REV_TI_EP 0 // Basic ti based edgeport
#define TI_BOARD_REV_COMPACT 1 // Compact board
#define TI_BOARD_REV_WATCHPORT 2 // Watchport
#define TI_GET_CPU_REVISION(x) (__u8)((((x)>>4)&0x0f))
#define TI_GET_BOARD_REVISION(x) (__u8)(((x)&0x0f))
#define TI_I2C_SIZE_MASK 0x1f // 5 bits
#define TI_GET_I2C_SIZE(x) ((((x) & TI_I2C_SIZE_MASK)+1)*256)
#define TI_MAX_I2C_SIZE (16 * 1024)
#define TI_MANUF_VERSION_0 0
// IonConig2 flags
#define TI_CONFIG2_RS232 0x01
#define TI_CONFIG2_RS422 0x02
#define TI_CONFIG2_RS485 0x04
#define TI_CONFIG2_SWITCHABLE 0x08
#define TI_CONFIG2_WATCHPORT 0x10
struct edge_ti_manuf_descriptor {
__u8 IonConfig; // Config byte for ION manufacturing use
__u8 IonConfig2; // Expansion
__u8 Version; // Version
__u8 CpuRev_BoardRev; // CPU revision level (0xF0) and Board Rev Level (0x0F)
__u8 NumPorts; // Number of ports for this UMP
__u8 NumVirtualPorts; // Number of Virtual ports
__u8 HubConfig1; // Used to configure the Hub
__u8 HubConfig2; // Used to configure the Hub
__u8 TotalPorts; // Total Number of Com Ports for the entire device (All UMPs)
__u8 Reserved; // Reserved
} __attribute__((packed));
#endif // if !defined(_USBVEND_H)

618
drivers/usb/serial/ipaq.c Normal file
View file

@ -0,0 +1,618 @@
/*
* USB Compaq iPAQ driver
*
* Copyright (C) 2001 - 2002
* Ganesh Varadarajan <ganesh@veritas.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#define KP_RETRIES 100
#define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
#define DRIVER_DESC "USB PocketPC PDA driver"
static int connect_retries = KP_RETRIES;
static int initial_wait;
/* Function prototypes for an ipaq */
static int ipaq_open(struct tty_struct *tty,
struct usb_serial_port *port);
static int ipaq_calc_num_ports(struct usb_serial *serial);
static int ipaq_startup(struct usb_serial *serial);
static const struct usb_device_id ipaq_id_table[] = {
{ USB_DEVICE(0x0104, 0x00BE) }, /* Socket USB Sync */
{ USB_DEVICE(0x03F0, 0x1016) }, /* HP USB Sync */
{ USB_DEVICE(0x03F0, 0x1116) }, /* HP USB Sync 1611 */
{ USB_DEVICE(0x03F0, 0x1216) }, /* HP USB Sync 1612 */
{ USB_DEVICE(0x03F0, 0x2016) }, /* HP USB Sync 1620 */
{ USB_DEVICE(0x03F0, 0x2116) }, /* HP USB Sync 1621 */
{ USB_DEVICE(0x03F0, 0x2216) }, /* HP USB Sync 1622 */
{ USB_DEVICE(0x03F0, 0x3016) }, /* HP USB Sync 1630 */
{ USB_DEVICE(0x03F0, 0x3116) }, /* HP USB Sync 1631 */
{ USB_DEVICE(0x03F0, 0x3216) }, /* HP USB Sync 1632 */
{ USB_DEVICE(0x03F0, 0x4016) }, /* HP USB Sync 1640 */
{ USB_DEVICE(0x03F0, 0x4116) }, /* HP USB Sync 1641 */
{ USB_DEVICE(0x03F0, 0x4216) }, /* HP USB Sync 1642 */
{ USB_DEVICE(0x03F0, 0x5016) }, /* HP USB Sync 1650 */
{ USB_DEVICE(0x03F0, 0x5116) }, /* HP USB Sync 1651 */
{ USB_DEVICE(0x03F0, 0x5216) }, /* HP USB Sync 1652 */
{ USB_DEVICE(0x0409, 0x00D5) }, /* NEC USB Sync */
{ USB_DEVICE(0x0409, 0x00D6) }, /* NEC USB Sync */
{ USB_DEVICE(0x0409, 0x00D7) }, /* NEC USB Sync */
{ USB_DEVICE(0x0409, 0x8024) }, /* NEC USB Sync */
{ USB_DEVICE(0x0409, 0x8025) }, /* NEC USB Sync */
{ USB_DEVICE(0x043E, 0x9C01) }, /* LGE USB Sync */
{ USB_DEVICE(0x045E, 0x00CE) }, /* Microsoft USB Sync */
{ USB_DEVICE(0x045E, 0x0400) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0401) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0402) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0403) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0404) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0405) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0406) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0407) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0408) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0409) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x040A) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x040B) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x040C) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x040D) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x040E) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x040F) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0410) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0411) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0412) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0413) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0414) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0415) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0416) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0417) }, /* Windows Powered Pocket PC 2002 */
{ USB_DEVICE(0x045E, 0x0432) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0433) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0434) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0435) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0436) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0437) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0438) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0439) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x043A) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x043B) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x043C) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x043D) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x043E) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x043F) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0440) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0441) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0442) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0443) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0444) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0445) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0446) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0447) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0448) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0449) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x044A) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x044B) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x044C) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x044D) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x044E) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x044F) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0450) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0451) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0452) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0453) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0454) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0455) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0456) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0457) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0458) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0459) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x045A) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x045B) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x045C) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x045D) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x045E) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x045F) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0460) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0461) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0462) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0463) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0464) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0465) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0466) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0467) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0468) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0469) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x046A) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x046B) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x046C) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x046D) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x046E) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x046F) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0470) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0471) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0472) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0473) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0474) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0475) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0476) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0477) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0478) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x0479) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x047A) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x047B) }, /* Windows Powered Pocket PC 2003 */
{ USB_DEVICE(0x045E, 0x04C8) }, /* Windows Powered Smartphone 2002 */
{ USB_DEVICE(0x045E, 0x04C9) }, /* Windows Powered Smartphone 2002 */
{ USB_DEVICE(0x045E, 0x04CA) }, /* Windows Powered Smartphone 2002 */
{ USB_DEVICE(0x045E, 0x04CB) }, /* Windows Powered Smartphone 2002 */
{ USB_DEVICE(0x045E, 0x04CC) }, /* Windows Powered Smartphone 2002 */
{ USB_DEVICE(0x045E, 0x04CD) }, /* Windows Powered Smartphone 2002 */
{ USB_DEVICE(0x045E, 0x04CE) }, /* Windows Powered Smartphone 2002 */
{ USB_DEVICE(0x045E, 0x04D7) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04D8) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04D9) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04DA) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04DB) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04DC) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04DD) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04DE) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04DF) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04E0) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04E1) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04E2) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04E3) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04E4) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04E5) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04E6) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04E7) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04E8) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04E9) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x045E, 0x04EA) }, /* Windows Powered Smartphone 2003 */
{ USB_DEVICE(0x049F, 0x0003) }, /* Compaq iPAQ USB Sync */
{ USB_DEVICE(0x049F, 0x0032) }, /* Compaq iPAQ USB Sync */
{ USB_DEVICE(0x04A4, 0x0014) }, /* Hitachi USB Sync */
{ USB_DEVICE(0x04AD, 0x0301) }, /* USB Sync 0301 */
{ USB_DEVICE(0x04AD, 0x0302) }, /* USB Sync 0302 */
{ USB_DEVICE(0x04AD, 0x0303) }, /* USB Sync 0303 */
{ USB_DEVICE(0x04AD, 0x0306) }, /* GPS Pocket PC USB Sync */
{ USB_DEVICE(0x04B7, 0x0531) }, /* MyGuide 7000 XL USB Sync */
{ USB_DEVICE(0x04C5, 0x1058) }, /* FUJITSU USB Sync */
{ USB_DEVICE(0x04C5, 0x1079) }, /* FUJITSU USB Sync */
{ USB_DEVICE(0x04DA, 0x2500) }, /* Panasonic USB Sync */
{ USB_DEVICE(0x04DD, 0x9102) }, /* SHARP WS003SH USB Modem */
{ USB_DEVICE(0x04DD, 0x9121) }, /* SHARP WS004SH USB Modem */
{ USB_DEVICE(0x04DD, 0x9123) }, /* SHARP WS007SH USB Modem */
{ USB_DEVICE(0x04DD, 0x9151) }, /* SHARP S01SH USB Modem */
{ USB_DEVICE(0x04DD, 0x91AC) }, /* SHARP WS011SH USB Modem */
{ USB_DEVICE(0x04E8, 0x5F00) }, /* Samsung NEXiO USB Sync */
{ USB_DEVICE(0x04E8, 0x5F01) }, /* Samsung NEXiO USB Sync */
{ USB_DEVICE(0x04E8, 0x5F02) }, /* Samsung NEXiO USB Sync */
{ USB_DEVICE(0x04E8, 0x5F03) }, /* Samsung NEXiO USB Sync */
{ USB_DEVICE(0x04E8, 0x5F04) }, /* Samsung NEXiO USB Sync */
{ USB_DEVICE(0x04E8, 0x6611) }, /* Samsung MITs USB Sync */
{ USB_DEVICE(0x04E8, 0x6613) }, /* Samsung MITs USB Sync */
{ USB_DEVICE(0x04E8, 0x6615) }, /* Samsung MITs USB Sync */
{ USB_DEVICE(0x04E8, 0x6617) }, /* Samsung MITs USB Sync */
{ USB_DEVICE(0x04E8, 0x6619) }, /* Samsung MITs USB Sync */
{ USB_DEVICE(0x04E8, 0x661B) }, /* Samsung MITs USB Sync */
{ USB_DEVICE(0x04E8, 0x662E) }, /* Samsung MITs USB Sync */
{ USB_DEVICE(0x04E8, 0x6630) }, /* Samsung MITs USB Sync */
{ USB_DEVICE(0x04E8, 0x6632) }, /* Samsung MITs USB Sync */
{ USB_DEVICE(0x04f1, 0x3011) }, /* JVC USB Sync */
{ USB_DEVICE(0x04F1, 0x3012) }, /* JVC USB Sync */
{ USB_DEVICE(0x0502, 0x1631) }, /* c10 Series */
{ USB_DEVICE(0x0502, 0x1632) }, /* c20 Series */
{ USB_DEVICE(0x0502, 0x16E1) }, /* Acer n10 Handheld USB Sync */
{ USB_DEVICE(0x0502, 0x16E2) }, /* Acer n20 Handheld USB Sync */
{ USB_DEVICE(0x0502, 0x16E3) }, /* Acer n30 Handheld USB Sync */
{ USB_DEVICE(0x0536, 0x01A0) }, /* HHP PDT */
{ USB_DEVICE(0x0543, 0x0ED9) }, /* ViewSonic Color Pocket PC V35 */
{ USB_DEVICE(0x0543, 0x1527) }, /* ViewSonic Color Pocket PC V36 */
{ USB_DEVICE(0x0543, 0x1529) }, /* ViewSonic Color Pocket PC V37 */
{ USB_DEVICE(0x0543, 0x152B) }, /* ViewSonic Color Pocket PC V38 */
{ USB_DEVICE(0x0543, 0x152E) }, /* ViewSonic Pocket PC */
{ USB_DEVICE(0x0543, 0x1921) }, /* ViewSonic Communicator Pocket PC */
{ USB_DEVICE(0x0543, 0x1922) }, /* ViewSonic Smartphone */
{ USB_DEVICE(0x0543, 0x1923) }, /* ViewSonic Pocket PC V30 */
{ USB_DEVICE(0x05E0, 0x2000) }, /* Symbol USB Sync */
{ USB_DEVICE(0x05E0, 0x2001) }, /* Symbol USB Sync 0x2001 */
{ USB_DEVICE(0x05E0, 0x2002) }, /* Symbol USB Sync 0x2002 */
{ USB_DEVICE(0x05E0, 0x2003) }, /* Symbol USB Sync 0x2003 */
{ USB_DEVICE(0x05E0, 0x2004) }, /* Symbol USB Sync 0x2004 */
{ USB_DEVICE(0x05E0, 0x2005) }, /* Symbol USB Sync 0x2005 */
{ USB_DEVICE(0x05E0, 0x2006) }, /* Symbol USB Sync 0x2006 */
{ USB_DEVICE(0x05E0, 0x2007) }, /* Symbol USB Sync 0x2007 */
{ USB_DEVICE(0x05E0, 0x2008) }, /* Symbol USB Sync 0x2008 */
{ USB_DEVICE(0x05E0, 0x2009) }, /* Symbol USB Sync 0x2009 */
{ USB_DEVICE(0x05E0, 0x200A) }, /* Symbol USB Sync 0x200A */
{ USB_DEVICE(0x067E, 0x1001) }, /* Intermec Mobile Computer */
{ USB_DEVICE(0x07CF, 0x2001) }, /* CASIO USB Sync 2001 */
{ USB_DEVICE(0x07CF, 0x2002) }, /* CASIO USB Sync 2002 */
{ USB_DEVICE(0x07CF, 0x2003) }, /* CASIO USB Sync 2003 */
{ USB_DEVICE(0x0930, 0x0700) }, /* TOSHIBA USB Sync 0700 */
{ USB_DEVICE(0x0930, 0x0705) }, /* TOSHIBA Pocket PC e310 */
{ USB_DEVICE(0x0930, 0x0706) }, /* TOSHIBA Pocket PC e740 */
{ USB_DEVICE(0x0930, 0x0707) }, /* TOSHIBA Pocket PC e330 Series */
{ USB_DEVICE(0x0930, 0x0708) }, /* TOSHIBA Pocket PC e350 Series */
{ USB_DEVICE(0x0930, 0x0709) }, /* TOSHIBA Pocket PC e750 Series */
{ USB_DEVICE(0x0930, 0x070A) }, /* TOSHIBA Pocket PC e400 Series */
{ USB_DEVICE(0x0930, 0x070B) }, /* TOSHIBA Pocket PC e800 Series */
{ USB_DEVICE(0x094B, 0x0001) }, /* Linkup Systems USB Sync */
{ USB_DEVICE(0x0960, 0x0065) }, /* BCOM USB Sync 0065 */
{ USB_DEVICE(0x0960, 0x0066) }, /* BCOM USB Sync 0066 */
{ USB_DEVICE(0x0960, 0x0067) }, /* BCOM USB Sync 0067 */
{ USB_DEVICE(0x0961, 0x0010) }, /* Portatec USB Sync */
{ USB_DEVICE(0x099E, 0x0052) }, /* Trimble GeoExplorer */
{ USB_DEVICE(0x099E, 0x4000) }, /* TDS Data Collector */
{ USB_DEVICE(0x0B05, 0x4200) }, /* ASUS USB Sync */
{ USB_DEVICE(0x0B05, 0x4201) }, /* ASUS USB Sync */
{ USB_DEVICE(0x0B05, 0x4202) }, /* ASUS USB Sync */
{ USB_DEVICE(0x0B05, 0x420F) }, /* ASUS USB Sync */
{ USB_DEVICE(0x0B05, 0x9200) }, /* ASUS USB Sync */
{ USB_DEVICE(0x0B05, 0x9202) }, /* ASUS USB Sync */
{ USB_DEVICE(0x0BB4, 0x00CE) }, /* HTC USB Sync */
{ USB_DEVICE(0x0BB4, 0x00CF) }, /* HTC USB Modem */
{ USB_DEVICE(0x0BB4, 0x0A01) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A02) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A03) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A04) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A05) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A06) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A07) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A08) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A09) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A0A) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A0B) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A0C) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A0D) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A0E) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A0F) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A10) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A11) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A12) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A13) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A14) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A15) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A16) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A17) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A18) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A19) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A1A) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A1B) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A1C) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A1D) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A1E) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A1F) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A20) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A21) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A22) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A23) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A24) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A25) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A26) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A27) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A28) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A29) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A2A) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A2B) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A2C) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A2D) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A2E) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A2F) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A30) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A31) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A32) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A33) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A34) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A35) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A36) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A37) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A38) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A39) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A3A) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A3B) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A3C) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A3D) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A3E) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A3F) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A40) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A41) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A42) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A43) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A44) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A45) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A46) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A47) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A48) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A49) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A4A) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A4B) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A4C) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A4D) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A4E) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A4F) }, /* PocketPC USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A50) }, /* HTC SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A51) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A52) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A53) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A54) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A55) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A56) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A57) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A58) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A59) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A5A) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A5B) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A5C) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A5D) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A5E) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A5F) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A60) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A61) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A62) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A63) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A64) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A65) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A66) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A67) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A68) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A69) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A6A) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A6B) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A6C) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A6D) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A6E) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A6F) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A70) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A71) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A72) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A73) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A74) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A75) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A76) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A77) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A78) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A79) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A7A) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A7B) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A7C) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A7D) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A7E) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A7F) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A80) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A81) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A82) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A83) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A84) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A85) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A86) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A87) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A88) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A89) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A8A) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A8B) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A8C) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A8D) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A8E) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A8F) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A90) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A91) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A92) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A93) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A94) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A95) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A96) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A97) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A98) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A99) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A9A) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A9B) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A9C) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A9D) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A9E) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A9F) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0BCE) }, /* "High Tech Computer Corp" */
{ USB_DEVICE(0x0BF8, 0x1001) }, /* Fujitsu Siemens Computers USB Sync */
{ USB_DEVICE(0x0C44, 0x03A2) }, /* Motorola iDEN Smartphone */
{ USB_DEVICE(0x0C8E, 0x6000) }, /* Cesscom Luxian Series */
{ USB_DEVICE(0x0CAD, 0x9001) }, /* Motorola PowerPad Pocket PC Device */
{ USB_DEVICE(0x0F4E, 0x0200) }, /* Freedom Scientific USB Sync */
{ USB_DEVICE(0x0F98, 0x0201) }, /* Cyberbank USB Sync */
{ USB_DEVICE(0x0FB8, 0x3001) }, /* Wistron USB Sync */
{ USB_DEVICE(0x0FB8, 0x3002) }, /* Wistron USB Sync */
{ USB_DEVICE(0x0FB8, 0x3003) }, /* Wistron USB Sync */
{ USB_DEVICE(0x0FB8, 0x4001) }, /* Wistron USB Sync */
{ USB_DEVICE(0x1066, 0x00CE) }, /* E-TEN USB Sync */
{ USB_DEVICE(0x1066, 0x0300) }, /* E-TEN P3XX Pocket PC */
{ USB_DEVICE(0x1066, 0x0500) }, /* E-TEN P5XX Pocket PC */
{ USB_DEVICE(0x1066, 0x0600) }, /* E-TEN P6XX Pocket PC */
{ USB_DEVICE(0x1066, 0x0700) }, /* E-TEN P7XX Pocket PC */
{ USB_DEVICE(0x1114, 0x0001) }, /* Psion Teklogix Sync 753x */
{ USB_DEVICE(0x1114, 0x0004) }, /* Psion Teklogix Sync netBookPro */
{ USB_DEVICE(0x1114, 0x0006) }, /* Psion Teklogix Sync 7525 */
{ USB_DEVICE(0x1182, 0x1388) }, /* VES USB Sync */
{ USB_DEVICE(0x11D9, 0x1002) }, /* Rugged Pocket PC 2003 */
{ USB_DEVICE(0x11D9, 0x1003) }, /* Rugged Pocket PC 2003 */
{ USB_DEVICE(0x1231, 0xCE01) }, /* USB Sync 03 */
{ USB_DEVICE(0x1231, 0xCE02) }, /* USB Sync 03 */
{ USB_DEVICE(0x1690, 0x0601) }, /* Askey USB Sync */
{ USB_DEVICE(0x22B8, 0x4204) }, /* Motorola MPx200 Smartphone */
{ USB_DEVICE(0x22B8, 0x4214) }, /* Motorola MPc GSM */
{ USB_DEVICE(0x22B8, 0x4224) }, /* Motorola MPx220 Smartphone */
{ USB_DEVICE(0x22B8, 0x4234) }, /* Motorola MPc CDMA */
{ USB_DEVICE(0x22B8, 0x4244) }, /* Motorola MPx100 Smartphone */
{ USB_DEVICE(0x3340, 0x011C) }, /* Mio DigiWalker PPC StrongARM */
{ USB_DEVICE(0x3340, 0x0326) }, /* Mio DigiWalker 338 */
{ USB_DEVICE(0x3340, 0x0426) }, /* Mio DigiWalker 338 */
{ USB_DEVICE(0x3340, 0x043A) }, /* Mio DigiWalker USB Sync */
{ USB_DEVICE(0x3340, 0x051C) }, /* MiTAC USB Sync 528 */
{ USB_DEVICE(0x3340, 0x053A) }, /* Mio DigiWalker SmartPhone USB Sync */
{ USB_DEVICE(0x3340, 0x071C) }, /* MiTAC USB Sync */
{ USB_DEVICE(0x3340, 0x0B1C) }, /* Generic PPC StrongARM */
{ USB_DEVICE(0x3340, 0x0E3A) }, /* Generic PPC USB Sync */
{ USB_DEVICE(0x3340, 0x0F1C) }, /* Itautec USB Sync */
{ USB_DEVICE(0x3340, 0x0F3A) }, /* Generic SmartPhone USB Sync */
{ USB_DEVICE(0x3340, 0x1326) }, /* Itautec USB Sync */
{ USB_DEVICE(0x3340, 0x191C) }, /* YAKUMO USB Sync */
{ USB_DEVICE(0x3340, 0x2326) }, /* Vobis USB Sync */
{ USB_DEVICE(0x3340, 0x3326) }, /* MEDION Winodws Moble USB Sync */
{ USB_DEVICE(0x3708, 0x20CE) }, /* Legend USB Sync */
{ USB_DEVICE(0x3708, 0x21CE) }, /* Lenovo USB Sync */
{ USB_DEVICE(0x4113, 0x0210) }, /* Mobile Media Technology USB Sync */
{ USB_DEVICE(0x4113, 0x0211) }, /* Mobile Media Technology USB Sync */
{ USB_DEVICE(0x4113, 0x0400) }, /* Mobile Media Technology USB Sync */
{ USB_DEVICE(0x4113, 0x0410) }, /* Mobile Media Technology USB Sync */
{ USB_DEVICE(0x413C, 0x4001) }, /* Dell Axim USB Sync */
{ USB_DEVICE(0x413C, 0x4002) }, /* Dell Axim USB Sync */
{ USB_DEVICE(0x413C, 0x4003) }, /* Dell Axim USB Sync */
{ USB_DEVICE(0x413C, 0x4004) }, /* Dell Axim USB Sync */
{ USB_DEVICE(0x413C, 0x4005) }, /* Dell Axim USB Sync */
{ USB_DEVICE(0x413C, 0x4006) }, /* Dell Axim USB Sync */
{ USB_DEVICE(0x413C, 0x4007) }, /* Dell Axim USB Sync */
{ USB_DEVICE(0x413C, 0x4008) }, /* Dell Axim USB Sync */
{ USB_DEVICE(0x413C, 0x4009) }, /* Dell Axim USB Sync */
{ USB_DEVICE(0x4505, 0x0010) }, /* Smartphone */
{ USB_DEVICE(0x5E04, 0xCE00) }, /* SAGEM Wireless Assistant */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ipaq_id_table);
/* All of the device info needed for the Compaq iPAQ */
static struct usb_serial_driver ipaq_device = {
.driver = {
.owner = THIS_MODULE,
.name = "ipaq",
},
.description = "PocketPC PDA",
.id_table = ipaq_id_table,
.bulk_in_size = 256,
.bulk_out_size = 256,
.open = ipaq_open,
.attach = ipaq_startup,
.calc_num_ports = ipaq_calc_num_ports,
};
static struct usb_serial_driver * const serial_drivers[] = {
&ipaq_device, NULL
};
static int ipaq_open(struct tty_struct *tty,
struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
int result = 0;
int retries = connect_retries;
msleep(1000*initial_wait);
/*
* Send out control message observed in win98 sniffs. Not sure what
* it does, but from empirical observations, it seems that the device
* will start the chat sequence once one of these messages gets
* through. Since this has a reasonably high failure rate, we retry
* several times.
*/
while (retries--) {
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
0x1, 0, NULL, 0, 100);
if (!result)
break;
msleep(1000);
}
if (!retries && result) {
dev_err(&port->dev, "%s - failed doing control urb, error %d\n",
__func__, result);
return result;
}
return usb_serial_generic_open(tty, port);
}
static int ipaq_calc_num_ports(struct usb_serial *serial)
{
/*
* some devices have 3 endpoints, the 3rd of which
* must be ignored as it would make the core
* create a second port which oopses when used
*/
int ipaq_num_ports = 1;
dev_dbg(&serial->dev->dev, "%s - numberofendpoints: %d\n", __func__,
(int)serial->interface->cur_altsetting->desc.bNumEndpoints);
/*
* a few devices have 4 endpoints, seemingly Yakuma devices,
* and we need the second pair, so let them have 2 ports
*
* TODO: can we drop port 1 ?
*/
if (serial->interface->cur_altsetting->desc.bNumEndpoints > 3) {
ipaq_num_ports = 2;
}
return ipaq_num_ports;
}
static int ipaq_startup(struct usb_serial *serial)
{
/* Some of the devices in ipaq_id_table[] are composite, and we
* shouldn't bind to all the interfaces. This test will rule out
* some obviously invalid possibilities.
*/
if (serial->num_bulk_in < serial->num_ports ||
serial->num_bulk_out < serial->num_ports)
return -ENODEV;
if (serial->dev->actconfig->desc.bConfigurationValue != 1) {
/*
* FIXME: HP iPaq rx3715, possibly others, have 1 config that
* is labeled as 2
*/
dev_err(&serial->dev->dev, "active config #%d != 1 ??\n",
serial->dev->actconfig->desc.bConfigurationValue);
return -ENODEV;
}
dev_dbg(&serial->dev->dev,
"%s - iPAQ module configured for %d ports\n", __func__,
serial->num_ports);
return usb_reset_configuration(serial->dev);
}
module_usb_serial_driver(serial_drivers, ipaq_id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(connect_retries, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(connect_retries,
"Maximum number of connect retries (one second each)");
module_param(initial_wait, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(initial_wait,
"Time to wait before attempting a connection (in seconds)");

317
drivers/usb/serial/ipw.c Normal file
View file

@ -0,0 +1,317 @@
/*
* IPWireless 3G UMTS TDD Modem driver (USB connected)
*
* Copyright (C) 2004 Roelf Diedericks <roelfd@inet.co.za>
* Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* All information about the device was acquired using SnoopyPro
* on MSFT's O/S, and examing the MSFT drivers' debug output
* (insanely left _on_ in the enduser version)
*
* It was written out of frustration with the IPWireless USB modem
* supplied by Axity3G/Sentech South Africa not supporting
* Linux whatsoever.
*
* Nobody provided any proprietary information that was not already
* available for this device.
*
* The modem adheres to the "3GPP TS 27.007 AT command set for 3G
* User Equipment (UE)" standard, available from
* http://www.3gpp.org/ftp/Specs/html-info/27007.htm
*
* The code was only tested the IPWireless handheld modem distributed
* in South Africa by Sentech.
*
* It may work for Woosh Inc in .nz too, as it appears they use the
* same kit.
*
* There is still some work to be done in terms of handling
* DCD, DTR, RTS, CTS which are currently faked.
* It's good enough for PPP at this point. It's based off all kinds of
* code found in usb/serial and usb/class
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
#include "usb-wwan.h"
#define DRIVER_AUTHOR "Roelf Diedericks"
#define DRIVER_DESC "IPWireless tty driver"
#define IPW_TTY_MAJOR 240 /* real device node major id, experimental range */
#define IPW_TTY_MINORS 256 /* we support 256 devices, dunno why, it'd be insane :) */
#define USB_IPW_MAGIC 0x6d02 /* magic number for ipw struct */
/* Message sizes */
#define EVENT_BUFFER_SIZE 0xFF
#define CHAR2INT16(c1, c0) (((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff))
/* vendor/product pairs that are known work with this driver*/
#define IPW_VID 0x0bc3
#define IPW_PID 0x0001
/* Vendor commands: */
/* baud rates */
enum {
ipw_sio_b256000 = 0x000e,
ipw_sio_b128000 = 0x001d,
ipw_sio_b115200 = 0x0020,
ipw_sio_b57600 = 0x0040,
ipw_sio_b56000 = 0x0042,
ipw_sio_b38400 = 0x0060,
ipw_sio_b19200 = 0x00c0,
ipw_sio_b14400 = 0x0100,
ipw_sio_b9600 = 0x0180,
ipw_sio_b4800 = 0x0300,
ipw_sio_b2400 = 0x0600,
ipw_sio_b1200 = 0x0c00,
ipw_sio_b600 = 0x1800
};
/* data bits */
#define ipw_dtb_7 0x700
#define ipw_dtb_8 0x810 /* ok so the define is misleading, I know, but forces 8,n,1 */
/* I mean, is there a point to any other setting these days? :) */
/* usb control request types : */
#define IPW_SIO_RXCTL 0x00 /* control bulk rx channel transmissions, value=1/0 (on/off) */
#define IPW_SIO_SET_BAUD 0x01 /* set baud, value=requested ipw_sio_bxxxx */
#define IPW_SIO_SET_LINE 0x03 /* set databits, parity. value=ipw_dtb_x */
#define IPW_SIO_SET_PIN 0x03 /* set/clear dtr/rts value=ipw_pin_xxx */
#define IPW_SIO_POLL 0x08 /* get serial port status byte, call with value=0 */
#define IPW_SIO_INIT 0x11 /* initializes ? value=0 (appears as first thing todo on open) */
#define IPW_SIO_PURGE 0x12 /* purge all transmissions?, call with value=numchar_to_purge */
#define IPW_SIO_HANDFLOW 0x13 /* set xon/xoff limits value=0, and a buffer of 0x10 bytes */
#define IPW_SIO_SETCHARS 0x13 /* set the flowcontrol special chars, value=0, buf=6 bytes, */
/* last 2 bytes contain flowcontrol chars e.g. 00 00 00 00 11 13 */
/* values used for request IPW_SIO_SET_PIN */
#define IPW_PIN_SETDTR 0x101
#define IPW_PIN_SETRTS 0x202
#define IPW_PIN_CLRDTR 0x100
#define IPW_PIN_CLRRTS 0x200 /* unconfirmed */
/* values used for request IPW_SIO_RXCTL */
#define IPW_RXBULK_ON 1
#define IPW_RXBULK_OFF 0
/* various 16 byte hardcoded transferbuffers used by flow control */
#define IPW_BYTES_FLOWINIT { 0x01, 0, 0, 0, 0x40, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0 }
/* Interpretation of modem status lines */
/* These need sorting out by individually connecting pins and checking
* results. FIXME!
* When data is being sent we see 0x30 in the lower byte; this must
* contain DSR and CTS ...
*/
#define IPW_DSR ((1<<4) | (1<<5))
#define IPW_CTS ((1<<5) | (1<<4))
#define IPW_WANTS_TO_SEND 0x30
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(IPW_VID, IPW_PID) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_device *udev = port->serial->dev;
struct device *dev = &port->dev;
u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT;
u8 *buf_flow_init;
int result;
buf_flow_init = kmemdup(buf_flow_static, 16, GFP_KERNEL);
if (!buf_flow_init)
return -ENOMEM;
/* --1: Tell the modem to initialize (we think) From sniffs this is
* always the first thing that gets sent to the modem during
* opening of the device */
dev_dbg(dev, "%s: Sending SIO_INIT (we guess)\n", __func__);
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_INIT,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
0,
0, /* index */
NULL,
0,
100000);
if (result < 0)
dev_err(dev, "Init of modem failed (error = %d)\n", result);
/* reset the bulk pipes */
usb_clear_halt(udev, usb_rcvbulkpipe(udev, port->bulk_in_endpointAddress));
usb_clear_halt(udev, usb_sndbulkpipe(udev, port->bulk_out_endpointAddress));
/*--2: Start reading from the device */
dev_dbg(dev, "%s: setting up bulk read callback\n", __func__);
usb_wwan_open(tty, port);
/*--3: Tell the modem to open the floodgates on the rx bulk channel */
dev_dbg(dev, "%s:asking modem for RxRead (RXBULK_ON)\n", __func__);
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_RXCTL,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
IPW_RXBULK_ON,
0, /* index */
NULL,
0,
100000);
if (result < 0)
dev_err(dev, "Enabling bulk RxRead failed (error = %d)\n", result);
/*--4: setup the initial flowcontrol */
dev_dbg(dev, "%s:setting init flowcontrol (%s)\n", __func__, buf_flow_init);
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_HANDFLOW,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
0,
0,
buf_flow_init,
0x10,
200000);
if (result < 0)
dev_err(dev, "initial flowcontrol failed (error = %d)\n", result);
kfree(buf_flow_init);
return 0;
}
static int ipw_attach(struct usb_serial *serial)
{
struct usb_wwan_intf_private *data;
data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
if (!data)
return -ENOMEM;
spin_lock_init(&data->susp_lock);
usb_set_serial_data(serial, data);
return 0;
}
static void ipw_release(struct usb_serial *serial)
{
struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
usb_set_serial_data(serial, NULL);
kfree(data);
}
static void ipw_dtr_rts(struct usb_serial_port *port, int on)
{
struct usb_device *udev = port->serial->dev;
struct device *dev = &port->dev;
int result;
dev_dbg(dev, "%s: on = %d\n", __func__, on);
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_SET_PIN,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
on ? IPW_PIN_SETDTR : IPW_PIN_CLRDTR,
0,
NULL,
0,
200000);
if (result < 0)
dev_err(dev, "setting dtr failed (error = %d)\n", result);
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_SET_PIN, USB_TYPE_VENDOR |
USB_RECIP_INTERFACE | USB_DIR_OUT,
on ? IPW_PIN_SETRTS : IPW_PIN_CLRRTS,
0,
NULL,
0,
200000);
if (result < 0)
dev_err(dev, "setting rts failed (error = %d)\n", result);
}
static void ipw_close(struct usb_serial_port *port)
{
struct usb_device *udev = port->serial->dev;
struct device *dev = &port->dev;
int result;
/*--3: purge */
dev_dbg(dev, "%s:sending purge\n", __func__);
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_PURGE, USB_TYPE_VENDOR |
USB_RECIP_INTERFACE | USB_DIR_OUT,
0x03,
0,
NULL,
0,
200000);
if (result < 0)
dev_err(dev, "purge failed (error = %d)\n", result);
/* send RXBULK_off (tell modem to stop transmitting bulk data on
rx chan) */
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_RXCTL,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
IPW_RXBULK_OFF,
0, /* index */
NULL,
0,
100000);
if (result < 0)
dev_err(dev, "Disabling bulk RxRead failed (error = %d)\n", result);
usb_wwan_close(port);
}
static struct usb_serial_driver ipw_device = {
.driver = {
.owner = THIS_MODULE,
.name = "ipw",
},
.description = "IPWireless converter",
.id_table = id_table,
.num_ports = 1,
.open = ipw_open,
.close = ipw_close,
.attach = ipw_attach,
.release = ipw_release,
.port_probe = usb_wwan_port_probe,
.port_remove = usb_wwan_port_remove,
.dtr_rts = ipw_dtr_rts,
.write = usb_wwan_write,
};
static struct usb_serial_driver * const serial_drivers[] = {
&ipw_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
/* Module information */
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

444
drivers/usb/serial/ir-usb.c Normal file
View file

@ -0,0 +1,444 @@
/*
* USB IR Dongle driver
*
* Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2002 Gary Brubaker (xavyer@ix.netcom.com)
* Copyright (C) 2010 Johan Hovold (jhovold@gmail.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver allows a USB IrDA device to be used as a "dumb" serial device.
* This can be useful if you do not have access to a full IrDA stack on the
* other side of the connection. If you do have an IrDA stack on both devices,
* please use the usb-irda driver, as it contains the proper error checking and
* other goodness of a full IrDA stack.
*
* Portions of this driver were taken from drivers/net/irda/irda-usb.c, which
* was written by Roman Weissgaerber <weissg@vienna.at>, Dag Brattli
* <dag@brattli.net>, and Jean Tourrilhes <jt@hpl.hp.com>
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/usb/irda.h>
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Johan Hovold <jhovold@gmail.com>"
#define DRIVER_DESC "USB IR Dongle driver"
/* if overridden by the user, then use their value for the size of the read and
* write urbs */
static int buffer_size;
/* if overridden by the user, then use the specified number of XBOFs */
static int xbof = -1;
static int ir_startup (struct usb_serial *serial);
static int ir_open(struct tty_struct *tty, struct usb_serial_port *port);
static int ir_prepare_write_buffer(struct usb_serial_port *port,
void *dest, size_t size);
static void ir_process_read_urb(struct urb *urb);
static void ir_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios);
/* Not that this lot means you can only have one per system */
static u8 ir_baud;
static u8 ir_xbof;
static u8 ir_add_bof;
static const struct usb_device_id ir_id_table[] = {
{ USB_DEVICE(0x050f, 0x0180) }, /* KC Technology, KC-180 */
{ USB_DEVICE(0x08e9, 0x0100) }, /* XTNDAccess */
{ USB_DEVICE(0x09c4, 0x0011) }, /* ACTiSys ACT-IR2000U */
{ USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, USB_SUBCLASS_IRDA, 0) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ir_id_table);
static struct usb_serial_driver ir_device = {
.driver = {
.owner = THIS_MODULE,
.name = "ir-usb",
},
.description = "IR Dongle",
.id_table = ir_id_table,
.num_ports = 1,
.set_termios = ir_set_termios,
.attach = ir_startup,
.open = ir_open,
.prepare_write_buffer = ir_prepare_write_buffer,
.process_read_urb = ir_process_read_urb,
};
static struct usb_serial_driver * const serial_drivers[] = {
&ir_device, NULL
};
static inline void irda_usb_dump_class_desc(struct usb_serial *serial,
struct usb_irda_cs_descriptor *desc)
{
struct device *dev = &serial->dev->dev;
dev_dbg(dev, "bLength=%x\n", desc->bLength);
dev_dbg(dev, "bDescriptorType=%x\n", desc->bDescriptorType);
dev_dbg(dev, "bcdSpecRevision=%x\n", __le16_to_cpu(desc->bcdSpecRevision));
dev_dbg(dev, "bmDataSize=%x\n", desc->bmDataSize);
dev_dbg(dev, "bmWindowSize=%x\n", desc->bmWindowSize);
dev_dbg(dev, "bmMinTurnaroundTime=%d\n", desc->bmMinTurnaroundTime);
dev_dbg(dev, "wBaudRate=%x\n", __le16_to_cpu(desc->wBaudRate));
dev_dbg(dev, "bmAdditionalBOFs=%x\n", desc->bmAdditionalBOFs);
dev_dbg(dev, "bIrdaRateSniff=%x\n", desc->bIrdaRateSniff);
dev_dbg(dev, "bMaxUnicastList=%x\n", desc->bMaxUnicastList);
}
/*------------------------------------------------------------------*/
/*
* Function irda_usb_find_class_desc(dev, ifnum)
*
* Returns instance of IrDA class descriptor, or NULL if not found
*
* The class descriptor is some extra info that IrDA USB devices will
* offer to us, describing their IrDA characteristics. We will use that in
* irda_usb_init_qos()
*
* Based on the same function in drivers/net/irda/irda-usb.c
*/
static struct usb_irda_cs_descriptor *
irda_usb_find_class_desc(struct usb_serial *serial, unsigned int ifnum)
{
struct usb_device *dev = serial->dev;
struct usb_irda_cs_descriptor *desc;
int ret;
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (!desc)
return NULL;
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_CS_IRDA_GET_CLASS_DESC,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0, ifnum, desc, sizeof(*desc), 1000);
dev_dbg(&serial->dev->dev, "%s - ret=%d\n", __func__, ret);
if (ret < sizeof(*desc)) {
dev_dbg(&serial->dev->dev,
"%s - class descriptor read %s (%d)\n", __func__,
(ret < 0) ? "failed" : "too short", ret);
goto error;
}
if (desc->bDescriptorType != USB_DT_CS_IRDA) {
dev_dbg(&serial->dev->dev, "%s - bad class descriptor type\n",
__func__);
goto error;
}
irda_usb_dump_class_desc(serial, desc);
return desc;
error:
kfree(desc);
return NULL;
}
static u8 ir_xbof_change(u8 xbof)
{
u8 result;
/* reference irda-usb.c */
switch (xbof) {
case 48:
result = 0x10;
break;
case 28:
case 24:
result = 0x20;
break;
default:
case 12:
result = 0x30;
break;
case 5:
case 6:
result = 0x40;
break;
case 3:
result = 0x50;
break;
case 2:
result = 0x60;
break;
case 1:
result = 0x70;
break;
case 0:
result = 0x80;
break;
}
return(result);
}
static int ir_startup(struct usb_serial *serial)
{
struct usb_irda_cs_descriptor *irda_desc;
irda_desc = irda_usb_find_class_desc(serial, 0);
if (!irda_desc) {
dev_err(&serial->dev->dev,
"IRDA class descriptor not found, device not bound\n");
return -ENODEV;
}
dev_dbg(&serial->dev->dev,
"%s - Baud rates supported:%s%s%s%s%s%s%s%s%s\n",
__func__,
(irda_desc->wBaudRate & USB_IRDA_BR_2400) ? " 2400" : "",
(irda_desc->wBaudRate & USB_IRDA_BR_9600) ? " 9600" : "",
(irda_desc->wBaudRate & USB_IRDA_BR_19200) ? " 19200" : "",
(irda_desc->wBaudRate & USB_IRDA_BR_38400) ? " 38400" : "",
(irda_desc->wBaudRate & USB_IRDA_BR_57600) ? " 57600" : "",
(irda_desc->wBaudRate & USB_IRDA_BR_115200) ? " 115200" : "",
(irda_desc->wBaudRate & USB_IRDA_BR_576000) ? " 576000" : "",
(irda_desc->wBaudRate & USB_IRDA_BR_1152000) ? " 1152000" : "",
(irda_desc->wBaudRate & USB_IRDA_BR_4000000) ? " 4000000" : "");
switch (irda_desc->bmAdditionalBOFs) {
case USB_IRDA_AB_48:
ir_add_bof = 48;
break;
case USB_IRDA_AB_24:
ir_add_bof = 24;
break;
case USB_IRDA_AB_12:
ir_add_bof = 12;
break;
case USB_IRDA_AB_6:
ir_add_bof = 6;
break;
case USB_IRDA_AB_3:
ir_add_bof = 3;
break;
case USB_IRDA_AB_2:
ir_add_bof = 2;
break;
case USB_IRDA_AB_1:
ir_add_bof = 1;
break;
case USB_IRDA_AB_0:
ir_add_bof = 0;
break;
default:
break;
}
kfree(irda_desc);
return 0;
}
static int ir_open(struct tty_struct *tty, struct usb_serial_port *port)
{
int i;
for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
port->write_urbs[i]->transfer_flags = URB_ZERO_PACKET;
/* Start reading from the device */
return usb_serial_generic_open(tty, port);
}
static int ir_prepare_write_buffer(struct usb_serial_port *port,
void *dest, size_t size)
{
unsigned char *buf = dest;
int count;
/*
* The first byte of the packet we send to the device contains an
* inbound header which indicates an additional number of BOFs and
* a baud rate change.
*
* See section 5.4.2.2 of the USB IrDA spec.
*/
*buf = ir_xbof | ir_baud;
count = kfifo_out_locked(&port->write_fifo, buf + 1, size - 1,
&port->lock);
return count + 1;
}
static void ir_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
if (!urb->actual_length)
return;
/*
* The first byte of the packet we get from the device
* contains a busy indicator and baud rate change.
* See section 5.4.1.2 of the USB IrDA spec.
*/
if (*data & 0x0f)
ir_baud = *data & 0x0f;
if (urb->actual_length == 1)
return;
tty_insert_flip_string(&port->port, data + 1, urb->actual_length - 1);
tty_flip_buffer_push(&port->port);
}
static void ir_set_termios_callback(struct urb *urb)
{
kfree(urb->transfer_buffer);
if (urb->status)
dev_dbg(&urb->dev->dev, "%s - non-zero urb status: %d\n",
__func__, urb->status);
}
static void ir_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct urb *urb;
unsigned char *transfer_buffer;
int result;
speed_t baud;
int ir_baud;
baud = tty_get_baud_rate(tty);
/*
* FIXME, we should compare the baud request against the
* capability stated in the IR header that we got in the
* startup function.
*/
switch (baud) {
case 2400:
ir_baud = USB_IRDA_BR_2400;
break;
case 9600:
ir_baud = USB_IRDA_BR_9600;
break;
case 19200:
ir_baud = USB_IRDA_BR_19200;
break;
case 38400:
ir_baud = USB_IRDA_BR_38400;
break;
case 57600:
ir_baud = USB_IRDA_BR_57600;
break;
case 115200:
ir_baud = USB_IRDA_BR_115200;
break;
case 576000:
ir_baud = USB_IRDA_BR_576000;
break;
case 1152000:
ir_baud = USB_IRDA_BR_1152000;
break;
case 4000000:
ir_baud = USB_IRDA_BR_4000000;
break;
default:
ir_baud = USB_IRDA_BR_9600;
baud = 9600;
}
if (xbof == -1)
ir_xbof = ir_xbof_change(ir_add_bof);
else
ir_xbof = ir_xbof_change(xbof) ;
/* Only speed changes are supported */
tty_termios_copy_hw(&tty->termios, old_termios);
tty_encode_baud_rate(tty, baud, baud);
/*
* send the baud change out on an "empty" data packet
*/
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return;
transfer_buffer = kmalloc(1, GFP_KERNEL);
if (!transfer_buffer)
goto err_buf;
*transfer_buffer = ir_xbof | ir_baud;
usb_fill_bulk_urb(
urb,
port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
transfer_buffer,
1,
ir_set_termios_callback,
port);
urb->transfer_flags = URB_ZERO_PACKET;
result = usb_submit_urb(urb, GFP_KERNEL);
if (result) {
dev_err(&port->dev, "%s - failed to submit urb: %d\n",
__func__, result);
goto err_subm;
}
usb_free_urb(urb);
return;
err_subm:
kfree(transfer_buffer);
err_buf:
usb_free_urb(urb);
}
static int __init ir_init(void)
{
if (buffer_size) {
ir_device.bulk_in_size = buffer_size;
ir_device.bulk_out_size = buffer_size;
}
return usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, ir_id_table);
}
static void __exit ir_exit(void)
{
usb_serial_deregister_drivers(serial_drivers);
}
module_init(ir_init);
module_exit(ir_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(xbof, int, 0);
MODULE_PARM_DESC(xbof, "Force specific number of XBOFs");
module_param(buffer_size, int, 0);
MODULE_PARM_DESC(buffer_size, "Size of the transfer buffers");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,122 @@
/*
* Infinity Unlimited USB Phoenix driver
*
* Copyright (C) 2007 Alain Degreffe (eczema@ecze.com)
*
*
* Original code taken from iuutool ( Copyright (C) 2006 Juan Carlos Borrás )
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* And tested with help of WB Electronics
*
*/
#define IUU_USB_VENDOR_ID 0x104f
#define IUU_USB_PRODUCT_ID 0x0004
#define IUU_USB_OP_TIMEOUT 0x0200
/* Programmer commands */
#define IUU_NO_OPERATION 0x00
#define IUU_GET_FIRMWARE_VERSION 0x01
#define IUU_GET_PRODUCT_NAME 0x02
#define IUU_GET_STATE_REGISTER 0x03
#define IUU_SET_LED 0x04
#define IUU_WAIT_MUS 0x05
#define IUU_WAIT_MS 0x06
#define IUU_GET_LOADER_VERSION 0x50
#define IUU_RST_SET 0x52
#define IUU_RST_CLEAR 0x53
#define IUU_SET_VCC 0x59
#define IUU_UART_ENABLE 0x49
#define IUU_UART_DISABLE 0x4A
#define IUU_UART_WRITE_I2C 0x4C
#define IUU_UART_ESC 0x5E
#define IUU_UART_TRAP 0x54
#define IUU_UART_TRAP_BREAK 0x5B
#define IUU_UART_RX 0x56
#define IUU_AVR_ON 0x21
#define IUU_AVR_OFF 0x22
#define IUU_AVR_1CLK 0x23
#define IUU_AVR_RESET 0x24
#define IUU_AVR_RESET_PC 0x25
#define IUU_AVR_INC_PC 0x26
#define IUU_AVR_INCN_PC 0x27
#define IUU_AVR_PREAD 0x29
#define IUU_AVR_PREADN 0x2A
#define IUU_AVR_PWRITE 0x28
#define IUU_AVR_DREAD 0x2C
#define IUU_AVR_DREADN 0x2D
#define IUU_AVR_DWRITE 0x2B
#define IUU_AVR_PWRITEN 0x2E
#define IUU_EEPROM_ON 0x37
#define IUU_EEPROM_OFF 0x38
#define IUU_EEPROM_WRITE 0x39
#define IUU_EEPROM_WRITEX 0x3A
#define IUU_EEPROM_WRITE8 0x3B
#define IUU_EEPROM_WRITE16 0x3C
#define IUU_EEPROM_WRITEX32 0x3D
#define IUU_EEPROM_WRITEX64 0x3E
#define IUU_EEPROM_READ 0x3F
#define IUU_EEPROM_READX 0x40
#define IUU_EEPROM_BREAD 0x41
#define IUU_EEPROM_BREADX 0x42
#define IUU_PIC_CMD 0x0A
#define IUU_PIC_CMD_LOAD 0x0B
#define IUU_PIC_CMD_READ 0x0C
#define IUU_PIC_ON 0x0D
#define IUU_PIC_OFF 0x0E
#define IUU_PIC_RESET 0x16
#define IUU_PIC_INC_PC 0x0F
#define IUU_PIC_INCN_PC 0x10
#define IUU_PIC_PWRITE 0x11
#define IUU_PIC_PREAD 0x12
#define IUU_PIC_PREADN 0x13
#define IUU_PIC_DWRITE 0x14
#define IUU_PIC_DREAD 0x15
#define IUU_UART_NOP 0x00
#define IUU_UART_CHANGE 0x02
#define IUU_UART_TX 0x04
#define IUU_DELAY_MS 0x06
#define IUU_OPERATION_OK 0x00
#define IUU_DEVICE_NOT_FOUND 0x01
#define IUU_INVALID_HANDLE 0x02
#define IUU_INVALID_PARAMETER 0x03
#define IUU_INVALID_voidERFACE 0x04
#define IUU_INVALID_REQUEST_LENGTH 0x05
#define IUU_UART_NOT_ENABLED 0x06
#define IUU_WRITE_ERROR 0x07
#define IUU_READ_ERROR 0x08
#define IUU_TX_ERROR 0x09
#define IUU_RX_ERROR 0x0A
#define IUU_PARITY_NONE 0x00
#define IUU_PARITY_EVEN 0x01
#define IUU_PARITY_ODD 0x02
#define IUU_PARITY_MARK 0x03
#define IUU_PARITY_SPACE 0x04
#define IUU_SC_INSERTED 0x01
#define IUU_VERIFY_ERROR 0x02
#define IUU_SIM_INSERTED 0x04
#define IUU_TWO_STOP_BITS 0x00
#define IUU_ONE_STOP_BIT 0x20
#define IUU_BAUD_2400 0x0398
#define IUU_BAUD_9600 0x0298
#define IUU_BAUD_19200 0x0164
#define IUU_BAUD_28800 0x0198
#define IUU_BAUD_38400 0x01B2
#define IUU_BAUD_57600 0x0030
#define IUU_BAUD_115200 0x0098
#define IUU_CLK_3579000 3579000
#define IUU_CLK_3680000 3680000
#define IUU_CLK_6000000 6000000
#define IUU_FULLCARD_IN 0x01
#define IUU_DEV_ERROR 0x02
#define IUU_MINICARD_IN 0x04
#define IUU_VCC_5V 0x00
#define IUU_VCC_3V 0x01

2535
drivers/usb/serial/keyspan.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,629 @@
/*
Keyspan USB to Serial Converter driver
(C) Copyright (C) 2000-2001
Hugh Blemings <hugh@blemings.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
See http://blemings.org/hugh/keyspan.html for more information.
Code in this driver inspired by and in a number of places taken
from Brian Warner's original Keyspan-PDA driver.
This driver has been put together with the support of Innosys, Inc.
and Keyspan, Inc the manufacturers of the Keyspan USB-serial products.
Thanks Guys :)
Thanks to Paulus for miscellaneous tidy ups, some largish chunks
of much nicer and/or completely new code and (perhaps most uniquely)
having the patience to sit down and explain why and where he'd changed
stuff.
Tip 'o the hat to IBM (and previously Linuxcare :) for supporting
staff in their work on open source projects.
See keyspan.c for update history.
*/
#ifndef __LINUX_USB_SERIAL_KEYSPAN_H
#define __LINUX_USB_SERIAL_KEYSPAN_H
/* Function prototypes for Keyspan serial converter */
static int keyspan_open (struct tty_struct *tty,
struct usb_serial_port *port);
static void keyspan_close (struct usb_serial_port *port);
static void keyspan_dtr_rts (struct usb_serial_port *port, int on);
static int keyspan_startup (struct usb_serial *serial);
static void keyspan_disconnect (struct usb_serial *serial);
static void keyspan_release (struct usb_serial *serial);
static int keyspan_port_probe(struct usb_serial_port *port);
static int keyspan_port_remove(struct usb_serial_port *port);
static int keyspan_write_room (struct tty_struct *tty);
static int keyspan_write (struct tty_struct *tty,
struct usb_serial_port *port,
const unsigned char *buf,
int count);
static void keyspan_send_setup (struct usb_serial_port *port,
int reset_port);
static void keyspan_set_termios (struct tty_struct *tty,
struct usb_serial_port *port,
struct ktermios *old);
static void keyspan_break_ctl (struct tty_struct *tty,
int break_state);
static int keyspan_tiocmget (struct tty_struct *tty);
static int keyspan_tiocmset (struct tty_struct *tty,
unsigned int set,
unsigned int clear);
static int keyspan_fake_startup (struct usb_serial *serial);
static int keyspan_usa19_calc_baud (struct usb_serial_port *port,
u32 baud_rate, u32 baudclk,
u8 *rate_hi, u8 *rate_low,
u8 *prescaler, int portnum);
static int keyspan_usa19w_calc_baud (struct usb_serial_port *port,
u32 baud_rate, u32 baudclk,
u8 *rate_hi, u8 *rate_low,
u8 *prescaler, int portnum);
static int keyspan_usa28_calc_baud (struct usb_serial_port *port,
u32 baud_rate, u32 baudclk,
u8 *rate_hi, u8 *rate_low,
u8 *prescaler, int portnum);
static int keyspan_usa19hs_calc_baud (struct usb_serial_port *port,
u32 baud_rate, u32 baudclk,
u8 *rate_hi, u8 *rate_low,
u8 *prescaler, int portnum);
static int keyspan_usa28_send_setup (struct usb_serial *serial,
struct usb_serial_port *port,
int reset_port);
static int keyspan_usa26_send_setup (struct usb_serial *serial,
struct usb_serial_port *port,
int reset_port);
static int keyspan_usa49_send_setup (struct usb_serial *serial,
struct usb_serial_port *port,
int reset_port);
static int keyspan_usa90_send_setup (struct usb_serial *serial,
struct usb_serial_port *port,
int reset_port);
static int keyspan_usa67_send_setup (struct usb_serial *serial,
struct usb_serial_port *port,
int reset_port);
/* Values used for baud rate calculation - device specific */
#define KEYSPAN_INVALID_BAUD_RATE (-1)
#define KEYSPAN_BAUD_RATE_OK (0)
#define KEYSPAN_USA18X_BAUDCLK (12000000L) /* a guess */
#define KEYSPAN_USA19_BAUDCLK (12000000L)
#define KEYSPAN_USA19W_BAUDCLK (24000000L)
#define KEYSPAN_USA19HS_BAUDCLK (14769231L)
#define KEYSPAN_USA28_BAUDCLK (1843200L)
#define KEYSPAN_USA28X_BAUDCLK (12000000L)
#define KEYSPAN_USA49W_BAUDCLK (48000000L)
/* Some constants used to characterise each device. */
#define KEYSPAN_MAX_NUM_PORTS (4)
#define KEYSPAN_MAX_FLIPS (2)
/* Device info for the Keyspan serial converter, used
by the overall usb-serial probe function */
#define KEYSPAN_VENDOR_ID (0x06cd)
/* Product IDs for the products supported, pre-renumeration */
#define keyspan_usa18x_pre_product_id 0x0105
#define keyspan_usa19_pre_product_id 0x0103
#define keyspan_usa19qi_pre_product_id 0x010b
#define keyspan_mpr_pre_product_id 0x011b
#define keyspan_usa19qw_pre_product_id 0x0118
#define keyspan_usa19w_pre_product_id 0x0106
#define keyspan_usa28_pre_product_id 0x0101
#define keyspan_usa28x_pre_product_id 0x0102
#define keyspan_usa28xa_pre_product_id 0x0114
#define keyspan_usa28xb_pre_product_id 0x0113
#define keyspan_usa49w_pre_product_id 0x0109
#define keyspan_usa49wlc_pre_product_id 0x011a
/* Product IDs post-renumeration. Note that the 28x and 28xb
have the same id's post-renumeration but behave identically
so it's not an issue. As such, the 28xb is not listed in any
of the device tables. */
#define keyspan_usa18x_product_id 0x0112
#define keyspan_usa19_product_id 0x0107
#define keyspan_usa19qi_product_id 0x010c
#define keyspan_usa19hs_product_id 0x0121
#define keyspan_mpr_product_id 0x011c
#define keyspan_usa19qw_product_id 0x0119
#define keyspan_usa19w_product_id 0x0108
#define keyspan_usa28_product_id 0x010f
#define keyspan_usa28x_product_id 0x0110
#define keyspan_usa28xa_product_id 0x0115
#define keyspan_usa28xb_product_id 0x0110
#define keyspan_usa28xg_product_id 0x0135
#define keyspan_usa49w_product_id 0x010a
#define keyspan_usa49wlc_product_id 0x012a
#define keyspan_usa49wg_product_id 0x0131
struct keyspan_device_details {
/* product ID value */
int product_id;
enum {msg_usa26, msg_usa28, msg_usa49, msg_usa90, msg_usa67} msg_format;
/* Number of physical ports */
int num_ports;
/* 1 if endpoint flipping used on input, 0 if not */
int indat_endp_flip;
/* 1 if endpoint flipping used on output, 0 if not */
int outdat_endp_flip;
/* Table mapping input data endpoint IDs to physical
port number and flip if used */
int indat_endpoints[KEYSPAN_MAX_NUM_PORTS];
/* Same for output endpoints */
int outdat_endpoints[KEYSPAN_MAX_NUM_PORTS];
/* Input acknowledge endpoints */
int inack_endpoints[KEYSPAN_MAX_NUM_PORTS];
/* Output control endpoints */
int outcont_endpoints[KEYSPAN_MAX_NUM_PORTS];
/* Endpoint used for input status */
int instat_endpoint;
/* Endpoint used for input data 49WG only */
int indat_endpoint;
/* Endpoint used for global control functions */
int glocont_endpoint;
int (*calculate_baud_rate) (struct usb_serial_port *port,
u32 baud_rate, u32 baudclk,
u8 *rate_hi, u8 *rate_low, u8 *prescaler, int portnum);
u32 baudclk;
};
/* Now for each device type we setup the device detail
structure with the appropriate information (provided
in Keyspan's documentation) */
static const struct keyspan_device_details usa18x_device_details = {
.product_id = keyspan_usa18x_product_id,
.msg_format = msg_usa26,
.num_ports = 1,
.indat_endp_flip = 0,
.outdat_endp_flip = 1,
.indat_endpoints = {0x81},
.outdat_endpoints = {0x01},
.inack_endpoints = {0x85},
.outcont_endpoints = {0x05},
.instat_endpoint = 0x87,
.indat_endpoint = -1,
.glocont_endpoint = 0x07,
.calculate_baud_rate = keyspan_usa19w_calc_baud,
.baudclk = KEYSPAN_USA18X_BAUDCLK,
};
static const struct keyspan_device_details usa19_device_details = {
.product_id = keyspan_usa19_product_id,
.msg_format = msg_usa28,
.num_ports = 1,
.indat_endp_flip = 1,
.outdat_endp_flip = 1,
.indat_endpoints = {0x81},
.outdat_endpoints = {0x01},
.inack_endpoints = {0x83},
.outcont_endpoints = {0x03},
.instat_endpoint = 0x84,
.indat_endpoint = -1,
.glocont_endpoint = -1,
.calculate_baud_rate = keyspan_usa19_calc_baud,
.baudclk = KEYSPAN_USA19_BAUDCLK,
};
static const struct keyspan_device_details usa19qi_device_details = {
.product_id = keyspan_usa19qi_product_id,
.msg_format = msg_usa28,
.num_ports = 1,
.indat_endp_flip = 1,
.outdat_endp_flip = 1,
.indat_endpoints = {0x81},
.outdat_endpoints = {0x01},
.inack_endpoints = {0x83},
.outcont_endpoints = {0x03},
.instat_endpoint = 0x84,
.indat_endpoint = -1,
.glocont_endpoint = -1,
.calculate_baud_rate = keyspan_usa28_calc_baud,
.baudclk = KEYSPAN_USA19_BAUDCLK,
};
static const struct keyspan_device_details mpr_device_details = {
.product_id = keyspan_mpr_product_id,
.msg_format = msg_usa28,
.num_ports = 1,
.indat_endp_flip = 1,
.outdat_endp_flip = 1,
.indat_endpoints = {0x81},
.outdat_endpoints = {0x01},
.inack_endpoints = {0x83},
.outcont_endpoints = {0x03},
.instat_endpoint = 0x84,
.indat_endpoint = -1,
.glocont_endpoint = -1,
.calculate_baud_rate = keyspan_usa28_calc_baud,
.baudclk = KEYSPAN_USA19_BAUDCLK,
};
static const struct keyspan_device_details usa19qw_device_details = {
.product_id = keyspan_usa19qw_product_id,
.msg_format = msg_usa26,
.num_ports = 1,
.indat_endp_flip = 0,
.outdat_endp_flip = 1,
.indat_endpoints = {0x81},
.outdat_endpoints = {0x01},
.inack_endpoints = {0x85},
.outcont_endpoints = {0x05},
.instat_endpoint = 0x87,
.indat_endpoint = -1,
.glocont_endpoint = 0x07,
.calculate_baud_rate = keyspan_usa19w_calc_baud,
.baudclk = KEYSPAN_USA19W_BAUDCLK,
};
static const struct keyspan_device_details usa19w_device_details = {
.product_id = keyspan_usa19w_product_id,
.msg_format = msg_usa26,
.num_ports = 1,
.indat_endp_flip = 0,
.outdat_endp_flip = 1,
.indat_endpoints = {0x81},
.outdat_endpoints = {0x01},
.inack_endpoints = {0x85},
.outcont_endpoints = {0x05},
.instat_endpoint = 0x87,
.indat_endpoint = -1,
.glocont_endpoint = 0x07,
.calculate_baud_rate = keyspan_usa19w_calc_baud,
.baudclk = KEYSPAN_USA19W_BAUDCLK,
};
static const struct keyspan_device_details usa19hs_device_details = {
.product_id = keyspan_usa19hs_product_id,
.msg_format = msg_usa90,
.num_ports = 1,
.indat_endp_flip = 0,
.outdat_endp_flip = 0,
.indat_endpoints = {0x81},
.outdat_endpoints = {0x01},
.inack_endpoints = {-1},
.outcont_endpoints = {0x02},
.instat_endpoint = 0x82,
.indat_endpoint = -1,
.glocont_endpoint = -1,
.calculate_baud_rate = keyspan_usa19hs_calc_baud,
.baudclk = KEYSPAN_USA19HS_BAUDCLK,
};
static const struct keyspan_device_details usa28_device_details = {
.product_id = keyspan_usa28_product_id,
.msg_format = msg_usa28,
.num_ports = 2,
.indat_endp_flip = 1,
.outdat_endp_flip = 1,
.indat_endpoints = {0x81, 0x83},
.outdat_endpoints = {0x01, 0x03},
.inack_endpoints = {0x85, 0x86},
.outcont_endpoints = {0x05, 0x06},
.instat_endpoint = 0x87,
.indat_endpoint = -1,
.glocont_endpoint = 0x07,
.calculate_baud_rate = keyspan_usa28_calc_baud,
.baudclk = KEYSPAN_USA28_BAUDCLK,
};
static const struct keyspan_device_details usa28x_device_details = {
.product_id = keyspan_usa28x_product_id,
.msg_format = msg_usa26,
.num_ports = 2,
.indat_endp_flip = 0,
.outdat_endp_flip = 1,
.indat_endpoints = {0x81, 0x83},
.outdat_endpoints = {0x01, 0x03},
.inack_endpoints = {0x85, 0x86},
.outcont_endpoints = {0x05, 0x06},
.instat_endpoint = 0x87,
.indat_endpoint = -1,
.glocont_endpoint = 0x07,
.calculate_baud_rate = keyspan_usa19w_calc_baud,
.baudclk = KEYSPAN_USA28X_BAUDCLK,
};
static const struct keyspan_device_details usa28xa_device_details = {
.product_id = keyspan_usa28xa_product_id,
.msg_format = msg_usa26,
.num_ports = 2,
.indat_endp_flip = 0,
.outdat_endp_flip = 1,
.indat_endpoints = {0x81, 0x83},
.outdat_endpoints = {0x01, 0x03},
.inack_endpoints = {0x85, 0x86},
.outcont_endpoints = {0x05, 0x06},
.instat_endpoint = 0x87,
.indat_endpoint = -1,
.glocont_endpoint = 0x07,
.calculate_baud_rate = keyspan_usa19w_calc_baud,
.baudclk = KEYSPAN_USA28X_BAUDCLK,
};
static const struct keyspan_device_details usa28xg_device_details = {
.product_id = keyspan_usa28xg_product_id,
.msg_format = msg_usa67,
.num_ports = 2,
.indat_endp_flip = 0,
.outdat_endp_flip = 0,
.indat_endpoints = {0x84, 0x88},
.outdat_endpoints = {0x02, 0x06},
.inack_endpoints = {-1, -1},
.outcont_endpoints = {-1, -1},
.instat_endpoint = 0x81,
.indat_endpoint = -1,
.glocont_endpoint = 0x01,
.calculate_baud_rate = keyspan_usa19w_calc_baud,
.baudclk = KEYSPAN_USA28X_BAUDCLK,
};
/* We don't need a separate entry for the usa28xb as it appears as a 28x anyway */
static const struct keyspan_device_details usa49w_device_details = {
.product_id = keyspan_usa49w_product_id,
.msg_format = msg_usa49,
.num_ports = 4,
.indat_endp_flip = 0,
.outdat_endp_flip = 0,
.indat_endpoints = {0x81, 0x82, 0x83, 0x84},
.outdat_endpoints = {0x01, 0x02, 0x03, 0x04},
.inack_endpoints = {-1, -1, -1, -1},
.outcont_endpoints = {-1, -1, -1, -1},
.instat_endpoint = 0x87,
.indat_endpoint = -1,
.glocont_endpoint = 0x07,
.calculate_baud_rate = keyspan_usa19w_calc_baud,
.baudclk = KEYSPAN_USA49W_BAUDCLK,
};
static const struct keyspan_device_details usa49wlc_device_details = {
.product_id = keyspan_usa49wlc_product_id,
.msg_format = msg_usa49,
.num_ports = 4,
.indat_endp_flip = 0,
.outdat_endp_flip = 0,
.indat_endpoints = {0x81, 0x82, 0x83, 0x84},
.outdat_endpoints = {0x01, 0x02, 0x03, 0x04},
.inack_endpoints = {-1, -1, -1, -1},
.outcont_endpoints = {-1, -1, -1, -1},
.instat_endpoint = 0x87,
.indat_endpoint = -1,
.glocont_endpoint = 0x07,
.calculate_baud_rate = keyspan_usa19w_calc_baud,
.baudclk = KEYSPAN_USA19W_BAUDCLK,
};
static const struct keyspan_device_details usa49wg_device_details = {
.product_id = keyspan_usa49wg_product_id,
.msg_format = msg_usa49,
.num_ports = 4,
.indat_endp_flip = 0,
.outdat_endp_flip = 0,
.indat_endpoints = {-1, -1, -1, -1}, /* single 'global' data in EP */
.outdat_endpoints = {0x01, 0x02, 0x04, 0x06},
.inack_endpoints = {-1, -1, -1, -1},
.outcont_endpoints = {-1, -1, -1, -1},
.instat_endpoint = 0x81,
.indat_endpoint = 0x88,
.glocont_endpoint = 0x00, /* uses control EP */
.calculate_baud_rate = keyspan_usa19w_calc_baud,
.baudclk = KEYSPAN_USA19W_BAUDCLK,
};
static const struct keyspan_device_details *keyspan_devices[] = {
&usa18x_device_details,
&usa19_device_details,
&usa19qi_device_details,
&mpr_device_details,
&usa19qw_device_details,
&usa19w_device_details,
&usa19hs_device_details,
&usa28_device_details,
&usa28x_device_details,
&usa28xa_device_details,
&usa28xg_device_details,
/* 28xb not required as it renumerates as a 28x */
&usa49w_device_details,
&usa49wlc_device_details,
&usa49wg_device_details,
NULL,
};
static const struct usb_device_id keyspan_ids_combined[] = {
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19hs_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id)},
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)},
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, keyspan_ids_combined);
/* usb_device_id table for the pre-firmware download keyspan devices */
static const struct usb_device_id keyspan_pre_ids[] = {
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_pre_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_pre_product_id) },
{ } /* Terminating entry */
};
static const struct usb_device_id keyspan_1port_ids[] = {
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19hs_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_product_id) },
{ } /* Terminating entry */
};
static const struct usb_device_id keyspan_2port_ids[] = {
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) },
{ } /* Terminating entry */
};
static const struct usb_device_id keyspan_4port_ids[] = {
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id) },
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)},
{ } /* Terminating entry */
};
/* Structs for the devices, pre and post renumeration. */
static struct usb_serial_driver keyspan_pre_device = {
.driver = {
.owner = THIS_MODULE,
.name = "keyspan_no_firm",
},
.description = "Keyspan - (without firmware)",
.id_table = keyspan_pre_ids,
.num_ports = 1,
.attach = keyspan_fake_startup,
};
static struct usb_serial_driver keyspan_1port_device = {
.driver = {
.owner = THIS_MODULE,
.name = "keyspan_1",
},
.description = "Keyspan 1 port adapter",
.id_table = keyspan_1port_ids,
.num_ports = 1,
.open = keyspan_open,
.close = keyspan_close,
.dtr_rts = keyspan_dtr_rts,
.write = keyspan_write,
.write_room = keyspan_write_room,
.set_termios = keyspan_set_termios,
.break_ctl = keyspan_break_ctl,
.tiocmget = keyspan_tiocmget,
.tiocmset = keyspan_tiocmset,
.attach = keyspan_startup,
.disconnect = keyspan_disconnect,
.release = keyspan_release,
.port_probe = keyspan_port_probe,
.port_remove = keyspan_port_remove,
};
static struct usb_serial_driver keyspan_2port_device = {
.driver = {
.owner = THIS_MODULE,
.name = "keyspan_2",
},
.description = "Keyspan 2 port adapter",
.id_table = keyspan_2port_ids,
.num_ports = 2,
.open = keyspan_open,
.close = keyspan_close,
.dtr_rts = keyspan_dtr_rts,
.write = keyspan_write,
.write_room = keyspan_write_room,
.set_termios = keyspan_set_termios,
.break_ctl = keyspan_break_ctl,
.tiocmget = keyspan_tiocmget,
.tiocmset = keyspan_tiocmset,
.attach = keyspan_startup,
.disconnect = keyspan_disconnect,
.release = keyspan_release,
.port_probe = keyspan_port_probe,
.port_remove = keyspan_port_remove,
};
static struct usb_serial_driver keyspan_4port_device = {
.driver = {
.owner = THIS_MODULE,
.name = "keyspan_4",
},
.description = "Keyspan 4 port adapter",
.id_table = keyspan_4port_ids,
.num_ports = 4,
.open = keyspan_open,
.close = keyspan_close,
.dtr_rts = keyspan_dtr_rts,
.write = keyspan_write,
.write_room = keyspan_write_room,
.set_termios = keyspan_set_termios,
.break_ctl = keyspan_break_ctl,
.tiocmget = keyspan_tiocmget,
.tiocmset = keyspan_tiocmset,
.attach = keyspan_startup,
.disconnect = keyspan_disconnect,
.release = keyspan_release,
.port_probe = keyspan_port_probe,
.port_remove = keyspan_port_remove,
};
static struct usb_serial_driver * const serial_drivers[] = {
&keyspan_pre_device, &keyspan_1port_device,
&keyspan_2port_device, &keyspan_4port_device, NULL
};
#endif

View file

@ -0,0 +1,798 @@
/*
* USB Keyspan PDA / Xircom / Entregra Converter driver
*
* Copyright (C) 1999 - 2001 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 1999, 2000 Brian Warner <warner@lothar.com>
* Copyright (C) 2000 Al Borchers <borchers@steinerpoint.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/usb/ezusb.h>
/* make a simple define to handle if we are compiling keyspan_pda or xircom support */
#if defined(CONFIG_USB_SERIAL_KEYSPAN_PDA) || defined(CONFIG_USB_SERIAL_KEYSPAN_PDA_MODULE)
#define KEYSPAN
#else
#undef KEYSPAN
#endif
#if defined(CONFIG_USB_SERIAL_XIRCOM) || defined(CONFIG_USB_SERIAL_XIRCOM_MODULE)
#define XIRCOM
#else
#undef XIRCOM
#endif
#define DRIVER_AUTHOR "Brian Warner <warner@lothar.com>"
#define DRIVER_DESC "USB Keyspan PDA Converter driver"
struct keyspan_pda_private {
int tx_room;
int tx_throttled;
struct work_struct wakeup_work;
struct work_struct unthrottle_work;
struct usb_serial *serial;
struct usb_serial_port *port;
};
#define KEYSPAN_VENDOR_ID 0x06cd
#define KEYSPAN_PDA_FAKE_ID 0x0103
#define KEYSPAN_PDA_ID 0x0104 /* no clue */
/* For Xircom PGSDB9 and older Entregra version of the same device */
#define XIRCOM_VENDOR_ID 0x085a
#define XIRCOM_FAKE_ID 0x8027
#define XIRCOM_FAKE_ID_2 0x8025 /* "PGMFHUB" serial */
#define ENTREGRA_VENDOR_ID 0x1645
#define ENTREGRA_FAKE_ID 0x8093
static const struct usb_device_id id_table_combined[] = {
#ifdef KEYSPAN
{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_FAKE_ID) },
#endif
#ifdef XIRCOM
{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) },
{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) },
{ USB_DEVICE(ENTREGRA_VENDOR_ID, ENTREGRA_FAKE_ID) },
#endif
{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table_combined);
static const struct usb_device_id id_table_std[] = {
{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) },
{ } /* Terminating entry */
};
#ifdef KEYSPAN
static const struct usb_device_id id_table_fake[] = {
{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_FAKE_ID) },
{ } /* Terminating entry */
};
#endif
#ifdef XIRCOM
static const struct usb_device_id id_table_fake_xircom[] = {
{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) },
{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) },
{ USB_DEVICE(ENTREGRA_VENDOR_ID, ENTREGRA_FAKE_ID) },
{ }
};
#endif
static void keyspan_pda_wakeup_write(struct work_struct *work)
{
struct keyspan_pda_private *priv =
container_of(work, struct keyspan_pda_private, wakeup_work);
struct usb_serial_port *port = priv->port;
tty_port_tty_wakeup(&port->port);
}
static void keyspan_pda_request_unthrottle(struct work_struct *work)
{
struct keyspan_pda_private *priv =
container_of(work, struct keyspan_pda_private, unthrottle_work);
struct usb_serial *serial = priv->serial;
int result;
/* ask the device to tell us when the tx buffer becomes
sufficiently empty */
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
7, /* request_unthrottle */
USB_TYPE_VENDOR | USB_RECIP_INTERFACE
| USB_DIR_OUT,
16, /* value: threshold */
0, /* index */
NULL,
0,
2000);
if (result < 0)
dev_dbg(&serial->dev->dev, "%s - error %d from usb_control_msg\n",
__func__, result);
}
static void keyspan_pda_rx_interrupt(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
int retval;
int status = urb->status;
struct keyspan_pda_private *priv;
priv = usb_get_serial_port_data(port);
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status);
return;
default:
dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
goto exit;
}
/* see if the message is data or a status interrupt */
switch (data[0]) {
case 0:
/* rest of message is rx data */
if (urb->actual_length) {
tty_insert_flip_string(&port->port, data + 1,
urb->actual_length - 1);
tty_flip_buffer_push(&port->port);
}
break;
case 1:
/* status interrupt */
dev_dbg(&port->dev, "rx int, d1=%d, d2=%d\n", data[1], data[2]);
switch (data[1]) {
case 1: /* modemline change */
break;
case 2: /* tx unthrottle interrupt */
priv->tx_throttled = 0;
/* queue up a wakeup at scheduler time */
schedule_work(&priv->wakeup_work);
break;
default:
break;
}
break;
default:
break;
}
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
dev_err(&port->dev,
"%s - usb_submit_urb failed with result %d\n",
__func__, retval);
}
static void keyspan_pda_rx_throttle(struct tty_struct *tty)
{
/* stop receiving characters. We just turn off the URB request, and
let chars pile up in the device. If we're doing hardware
flowcontrol, the device will signal the other end when its buffer
fills up. If we're doing XON/XOFF, this would be a good time to
send an XOFF, although it might make sense to foist that off
upon the device too. */
struct usb_serial_port *port = tty->driver_data;
usb_kill_urb(port->interrupt_in_urb);
}
static void keyspan_pda_rx_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
/* just restart the receive interrupt URB */
if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL))
dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n");
}
static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud)
{
int rc;
int bindex;
switch (baud) {
case 110:
bindex = 0;
break;
case 300:
bindex = 1;
break;
case 1200:
bindex = 2;
break;
case 2400:
bindex = 3;
break;
case 4800:
bindex = 4;
break;
case 9600:
bindex = 5;
break;
case 19200:
bindex = 6;
break;
case 38400:
bindex = 7;
break;
case 57600:
bindex = 8;
break;
case 115200:
bindex = 9;
break;
default:
bindex = 5; /* Default to 9600 */
baud = 9600;
}
/* rather than figure out how to sleep while waiting for this
to complete, I just use the "legacy" API. */
rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
0, /* set baud */
USB_TYPE_VENDOR
| USB_RECIP_INTERFACE
| USB_DIR_OUT, /* type */
bindex, /* value */
0, /* index */
NULL, /* &data */
0, /* size */
2000); /* timeout */
if (rc < 0)
return 0;
return baud;
}
static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
int value;
int result;
if (break_state == -1)
value = 1; /* start break */
else
value = 0; /* clear break */
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
4, /* set break */
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
value, 0, NULL, 0, 2000);
if (result < 0)
dev_dbg(&port->dev, "%s - error %d from usb_control_msg\n",
__func__, result);
/* there is something funky about this.. the TCSBRK that 'cu' performs
ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4
seconds apart, but it feels like the break sent isn't as long as it
is on /dev/ttyS0 */
}
static void keyspan_pda_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct usb_serial *serial = port->serial;
speed_t speed;
/* cflag specifies lots of stuff: number of stop bits, parity, number
of data bits, baud. What can the device actually handle?:
CSTOPB (1 stop bit or 2)
PARENB (parity)
CSIZE (5bit .. 8bit)
There is minimal hw support for parity (a PSW bit seems to hold the
parity of whatever is in the accumulator). The UART either deals
with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data,
1 special, stop). So, with firmware changes, we could do:
8N1: 10 bit
8N2: 11 bit, extra bit always (mark?)
8[EOMS]1: 11 bit, extra bit is parity
7[EOMS]1: 10 bit, b0/b7 is parity
7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?)
HW flow control is dictated by the tty->termios.c_cflags & CRTSCTS
bit.
For now, just do baud. */
speed = tty_get_baud_rate(tty);
speed = keyspan_pda_setbaud(serial, speed);
if (speed == 0) {
dev_dbg(&port->dev, "can't handle requested baud rate\n");
/* It hasn't changed so.. */
speed = tty_termios_baud_rate(old_termios);
}
/* Only speed can change so copy the old h/w parameters
then encode the new speed */
tty_termios_copy_hw(&tty->termios, old_termios);
tty_encode_baud_rate(tty, speed, speed);
}
/* modem control pins: DTR and RTS are outputs and can be controlled.
DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be
read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused */
static int keyspan_pda_get_modem_info(struct usb_serial *serial,
unsigned char *value)
{
int rc;
u8 *data;
data = kmalloc(1, GFP_KERNEL);
if (!data)
return -ENOMEM;
rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
3, /* get pins */
USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN,
0, 0, data, 1, 2000);
if (rc >= 0)
*value = *data;
kfree(data);
return rc;
}
static int keyspan_pda_set_modem_info(struct usb_serial *serial,
unsigned char value)
{
int rc;
rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
3, /* set pins */
USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_OUT,
value, 0, NULL, 0, 2000);
return rc;
}
static int keyspan_pda_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
int rc;
unsigned char status;
int value;
rc = keyspan_pda_get_modem_info(serial, &status);
if (rc < 0)
return rc;
value =
((status & (1<<7)) ? TIOCM_DTR : 0) |
((status & (1<<6)) ? TIOCM_CAR : 0) |
((status & (1<<5)) ? TIOCM_RNG : 0) |
((status & (1<<4)) ? TIOCM_DSR : 0) |
((status & (1<<3)) ? TIOCM_CTS : 0) |
((status & (1<<2)) ? TIOCM_RTS : 0);
return value;
}
static int keyspan_pda_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
int rc;
unsigned char status;
rc = keyspan_pda_get_modem_info(serial, &status);
if (rc < 0)
return rc;
if (set & TIOCM_RTS)
status |= (1<<2);
if (set & TIOCM_DTR)
status |= (1<<7);
if (clear & TIOCM_RTS)
status &= ~(1<<2);
if (clear & TIOCM_DTR)
status &= ~(1<<7);
rc = keyspan_pda_set_modem_info(serial, status);
return rc;
}
static int keyspan_pda_write(struct tty_struct *tty,
struct usb_serial_port *port, const unsigned char *buf, int count)
{
struct usb_serial *serial = port->serial;
int request_unthrottle = 0;
int rc = 0;
struct keyspan_pda_private *priv;
priv = usb_get_serial_port_data(port);
/* guess how much room is left in the device's ring buffer, and if we
want to send more than that, check first, updating our notion of
what is left. If our write will result in no room left, ask the
device to give us an interrupt when the room available rises above
a threshold, and hold off all writers (eventually, those using
select() or poll() too) until we receive that unthrottle interrupt.
Block if we can't write anything at all, otherwise write as much as
we can. */
if (count == 0) {
dev_dbg(&port->dev, "write request of 0 bytes\n");
return 0;
}
/* we might block because of:
the TX urb is in-flight (wait until it completes)
the device is full (wait until it says there is room)
*/
spin_lock_bh(&port->lock);
if (!test_bit(0, &port->write_urbs_free) || priv->tx_throttled) {
spin_unlock_bh(&port->lock);
return 0;
}
clear_bit(0, &port->write_urbs_free);
spin_unlock_bh(&port->lock);
/* At this point the URB is in our control, nobody else can submit it
again (the only sudden transition was the one from EINPROGRESS to
finished). Also, the tx process is not throttled. So we are
ready to write. */
count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
/* Check if we might overrun the Tx buffer. If so, ask the
device how much room it really has. This is done only on
scheduler time, since usb_control_msg() sleeps. */
if (count > priv->tx_room && !in_interrupt()) {
u8 *room;
room = kmalloc(1, GFP_KERNEL);
if (!room) {
rc = -ENOMEM;
goto exit;
}
rc = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
6, /* write_room */
USB_TYPE_VENDOR | USB_RECIP_INTERFACE
| USB_DIR_IN,
0, /* value: 0 means "remaining room" */
0, /* index */
room,
1,
2000);
if (rc > 0) {
dev_dbg(&port->dev, "roomquery says %d\n", *room);
priv->tx_room = *room;
}
kfree(room);
if (rc < 0) {
dev_dbg(&port->dev, "roomquery failed\n");
goto exit;
}
if (rc == 0) {
dev_dbg(&port->dev, "roomquery returned 0 bytes\n");
rc = -EIO; /* device didn't return any data */
goto exit;
}
}
if (count > priv->tx_room) {
/* we're about to completely fill the Tx buffer, so
we'll be throttled afterwards. */
count = priv->tx_room;
request_unthrottle = 1;
}
if (count) {
/* now transfer data */
memcpy(port->write_urb->transfer_buffer, buf, count);
/* send the data out the bulk port */
port->write_urb->transfer_buffer_length = count;
priv->tx_room -= count;
rc = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (rc) {
dev_dbg(&port->dev, "usb_submit_urb(write bulk) failed\n");
goto exit;
}
} else {
/* There wasn't any room left, so we are throttled until
the buffer empties a bit */
request_unthrottle = 1;
}
if (request_unthrottle) {
priv->tx_throttled = 1; /* block writers */
schedule_work(&priv->unthrottle_work);
}
rc = count;
exit:
if (rc < 0)
set_bit(0, &port->write_urbs_free);
return rc;
}
static void keyspan_pda_write_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct keyspan_pda_private *priv;
set_bit(0, &port->write_urbs_free);
priv = usb_get_serial_port_data(port);
/* queue up a wakeup at scheduler time */
schedule_work(&priv->wakeup_work);
}
static int keyspan_pda_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct keyspan_pda_private *priv;
priv = usb_get_serial_port_data(port);
/* used by n_tty.c for processing of tabs and such. Giving it our
conservative guess is probably good enough, but needs testing by
running a console through the device. */
return priv->tx_room;
}
static int keyspan_pda_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct keyspan_pda_private *priv;
unsigned long flags;
int ret = 0;
priv = usb_get_serial_port_data(port);
/* when throttled, return at least WAKEUP_CHARS to tell select() (via
n_tty.c:normal_poll() ) that we're not writeable. */
spin_lock_irqsave(&port->lock, flags);
if (!test_bit(0, &port->write_urbs_free) || priv->tx_throttled)
ret = 256;
spin_unlock_irqrestore(&port->lock, flags);
return ret;
}
static void keyspan_pda_dtr_rts(struct usb_serial_port *port, int on)
{
struct usb_serial *serial = port->serial;
if (on)
keyspan_pda_set_modem_info(serial, (1 << 7) | (1 << 2));
else
keyspan_pda_set_modem_info(serial, 0);
}
static int keyspan_pda_open(struct tty_struct *tty,
struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
u8 *room;
int rc = 0;
struct keyspan_pda_private *priv;
/* find out how much room is in the Tx ring */
room = kmalloc(1, GFP_KERNEL);
if (!room)
return -ENOMEM;
rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
6, /* write_room */
USB_TYPE_VENDOR | USB_RECIP_INTERFACE
| USB_DIR_IN,
0, /* value */
0, /* index */
room,
1,
2000);
if (rc < 0) {
dev_dbg(&port->dev, "%s - roomquery failed\n", __func__);
goto error;
}
if (rc == 0) {
dev_dbg(&port->dev, "%s - roomquery returned 0 bytes\n", __func__);
rc = -EIO;
goto error;
}
priv = usb_get_serial_port_data(port);
priv->tx_room = *room;
priv->tx_throttled = *room ? 0 : 1;
/*Start reading from the device*/
rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (rc) {
dev_dbg(&port->dev, "%s - usb_submit_urb(read int) failed\n", __func__);
goto error;
}
error:
kfree(room);
return rc;
}
static void keyspan_pda_close(struct usb_serial_port *port)
{
usb_kill_urb(port->write_urb);
usb_kill_urb(port->interrupt_in_urb);
}
/* download the firmware to a "fake" device (pre-renumeration) */
static int keyspan_pda_fake_startup(struct usb_serial *serial)
{
int response;
const char *fw_name;
/* download the firmware here ... */
response = ezusb_fx1_set_reset(serial->dev, 1);
if (0) { ; }
#ifdef KEYSPAN
else if (le16_to_cpu(serial->dev->descriptor.idVendor) == KEYSPAN_VENDOR_ID)
fw_name = "keyspan_pda/keyspan_pda.fw";
#endif
#ifdef XIRCOM
else if ((le16_to_cpu(serial->dev->descriptor.idVendor) == XIRCOM_VENDOR_ID) ||
(le16_to_cpu(serial->dev->descriptor.idVendor) == ENTREGRA_VENDOR_ID))
fw_name = "keyspan_pda/xircom_pgs.fw";
#endif
else {
dev_err(&serial->dev->dev, "%s: unknown vendor, aborting.\n",
__func__);
return -ENODEV;
}
if (ezusb_fx1_ihex_firmware_download(serial->dev, fw_name) < 0) {
dev_err(&serial->dev->dev, "failed to load firmware \"%s\"\n",
fw_name);
return -ENOENT;
}
/* after downloading firmware Renumeration will occur in a
moment and the new device will bind to the real driver */
/* we want this device to fail to have a driver assigned to it. */
return 1;
}
#ifdef KEYSPAN
MODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw");
#endif
#ifdef XIRCOM
MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw");
#endif
static int keyspan_pda_port_probe(struct usb_serial_port *port)
{
struct keyspan_pda_private *priv;
priv = kmalloc(sizeof(struct keyspan_pda_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
INIT_WORK(&priv->wakeup_work, keyspan_pda_wakeup_write);
INIT_WORK(&priv->unthrottle_work, keyspan_pda_request_unthrottle);
priv->serial = port->serial;
priv->port = port;
usb_set_serial_port_data(port, priv);
return 0;
}
static int keyspan_pda_port_remove(struct usb_serial_port *port)
{
struct keyspan_pda_private *priv;
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
#ifdef KEYSPAN
static struct usb_serial_driver keyspan_pda_fake_device = {
.driver = {
.owner = THIS_MODULE,
.name = "keyspan_pda_pre",
},
.description = "Keyspan PDA - (prerenumeration)",
.id_table = id_table_fake,
.num_ports = 1,
.attach = keyspan_pda_fake_startup,
};
#endif
#ifdef XIRCOM
static struct usb_serial_driver xircom_pgs_fake_device = {
.driver = {
.owner = THIS_MODULE,
.name = "xircom_no_firm",
},
.description = "Xircom / Entregra PGS - (prerenumeration)",
.id_table = id_table_fake_xircom,
.num_ports = 1,
.attach = keyspan_pda_fake_startup,
};
#endif
static struct usb_serial_driver keyspan_pda_device = {
.driver = {
.owner = THIS_MODULE,
.name = "keyspan_pda",
},
.description = "Keyspan PDA",
.id_table = id_table_std,
.num_ports = 1,
.dtr_rts = keyspan_pda_dtr_rts,
.open = keyspan_pda_open,
.close = keyspan_pda_close,
.write = keyspan_pda_write,
.write_room = keyspan_pda_write_room,
.write_bulk_callback = keyspan_pda_write_bulk_callback,
.read_int_callback = keyspan_pda_rx_interrupt,
.chars_in_buffer = keyspan_pda_chars_in_buffer,
.throttle = keyspan_pda_rx_throttle,
.unthrottle = keyspan_pda_rx_unthrottle,
.set_termios = keyspan_pda_set_termios,
.break_ctl = keyspan_pda_break_ctl,
.tiocmget = keyspan_pda_tiocmget,
.tiocmset = keyspan_pda_tiocmset,
.port_probe = keyspan_pda_port_probe,
.port_remove = keyspan_pda_port_remove,
};
static struct usb_serial_driver * const serial_drivers[] = {
&keyspan_pda_device,
#ifdef KEYSPAN
&keyspan_pda_fake_device,
#endif
#ifdef XIRCOM
&xircom_pgs_fake_device,
#endif
NULL
};
module_usb_serial_driver(serial_drivers, id_table_combined);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,260 @@
/*
usa26msg.h
Copyright (C) 1998-2000 InnoSys Incorporated. All Rights Reserved
This file is available under a BSD-style copyright
Keyspan USB Async Message Formats for the USA28X
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain this licence text
without modification, this list of conditions, and the following
disclaimer. The following copyright notice must appear immediately at
the beginning of all source files:
Copyright (C) 1998-2000 InnoSys Incorporated. All Rights Reserved
This file is available under a BSD-style copyright
2. The name of InnoSys Incorporated may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Third revision: USA28X version (aka USA26)
Buffer formats for RX/TX data messages are not defined by
a structure, but are described here:
USB OUT (host -> USAxx, transmit) messages contain a
REQUEST_ACK indicator (set to 0xff to request an ACK at the
completion of transmit; 0x00 otherwise), followed by data:
RQSTACK DAT DAT DAT ...
with a total data length of 63.
USB IN (USAxx -> host, receive) messages begin with a status
byte in which the 0x80 bit is either:
(a) 0x80 bit clear
indicates that the bytes following it are all data
bytes:
STAT DATA DATA DATA DATA DATA ...
for a total of up to 63 DATA bytes,
or:
(b) 0x80 bit set
indicates that the bytes following alternate data and
status bytes:
STAT DATA STAT DATA STAT DATA STAT DATA ...
for a total of up to 32 DATA bytes.
The valid bits in the STAT bytes are:
OVERRUN 0x02
PARITY 0x04
FRAMING 0x08
BREAK 0x10
Notes:
(1) The OVERRUN bit can appear in either (a) or (b) format
messages, but the but the PARITY/FRAMING/BREAK bits
only appear in (b) format messages.
(2) For the host to determine the exact point at which the
overrun occurred (to identify the point in the data
stream at which the data was lost), it needs to count
128 characters, starting at the first character of the
message in which OVERRUN was reported; the lost character(s)
would have been received between the 128th and 129th
characters.
(3) An RX data message in which the first byte has 0x80 clear
serves as a "break off" indicator.
revision history:
1999feb10 add reportHskiaChanges to allow us to ignore them
1999feb10 add txAckThreshold for fast+loose throughput enhancement
1999mar30 beef up support for RX error reporting
1999apr14 add resetDataToggle to control message
2000jan04 merge with usa17msg.h
2000jun01 add extended BSD-style copyright text
2001jul05 change message format to improve OVERRUN case
Note on shared names:
In the case of fields which have been merged between the USA17
and USA26 definitions, the USA26 definition is the first part
of the name and the USA17 definition is the second part of the
name; both meanings are described below.
*/
#ifndef __USA26MSG__
#define __USA26MSG__
struct keyspan_usa26_portControlMessage
{
/*
there are three types of "commands" sent in the control message:
1. configuration changes which must be requested by setting
the corresponding "set" flag (and should only be requested
when necessary, to reduce overhead on the USA26):
*/
u8 setClocking, // BOTH: host requests baud rate be set
baudLo, // BOTH: host does baud divisor calculation
baudHi, // BOTH: baudHi is only used for first port (gives lower rates)
externalClock_txClocking,
// USA26: 0=internal, other=external
// USA17: 0=internal, other=external/RI
rxClocking, // USA17: 0=internal, 1=external/RI, other=external/DSR
setLcr, // BOTH: host requests lcr be set
lcr, // BOTH: use PARITY, STOPBITS, DATABITS below
setFlowControl, // BOTH: host requests flow control be set
ctsFlowControl, // BOTH: 1=use CTS flow control, 0=don't
xonFlowControl, // BOTH: 1=use XON/XOFF flow control, 0=don't
xonChar, // BOTH: specified in current character format
xoffChar, // BOTH: specified in current character format
setTxTriState_setRts,
// USA26: host requests TX tri-state be set
// USA17: host requests RTS output be set
txTriState_rts, // BOTH: 1=active (normal), 0=tristate (off)
setHskoa_setDtr,
// USA26: host requests HSKOA output be set
// USA17: host requests DTR output be set
hskoa_dtr, // BOTH: 1=on, 0=off
setPrescaler, // USA26: host requests prescalar be set (default: 13)
prescaler; // BOTH: specified as N/8; values 8-ff are valid
// must be set any time internal baud rate is set;
// must not be set when external clocking is used
// note: in USA17, prescaler is applied whenever
// setClocking is requested
/*
3. configuration data which is simply used as is (no overhead,
but must be specified correctly in every host message).
*/
u8 forwardingLength, // BOTH: forward when this number of chars available
reportHskiaChanges_dsrFlowControl,
// USA26: 1=normal; 0=ignore external clock
// USA17: 1=use DSR flow control, 0=don't
txAckThreshold, // BOTH: 0=not allowed, 1=normal, 2-255 deliver ACK faster
loopbackMode; // BOTH: 0=no loopback, 1=loopback enabled
/*
4. commands which are flags only; these are processed in order
(so that, e.g., if both _txOn and _txOff flags are set, the
port ends in a TX_OFF state); any non-zero value is respected
*/
u8 _txOn, // BOTH: enable transmitting (and continue if there's data)
_txOff, // BOTH: stop transmitting
txFlush, // BOTH: toss outbound data
txBreak, // BOTH: turn on break (cleared by _txOn)
rxOn, // BOTH: turn on receiver
rxOff, // BOTH: turn off receiver
rxFlush, // BOTH: toss inbound data
rxForward, // BOTH: forward all inbound data, NOW (as if fwdLen==1)
returnStatus, // BOTH: return current status (even if it hasn't changed)
resetDataToggle;// BOTH: reset data toggle state to DATA0
};
// defines for bits in lcr
#define USA_DATABITS_5 0x00
#define USA_DATABITS_6 0x01
#define USA_DATABITS_7 0x02
#define USA_DATABITS_8 0x03
#define STOPBITS_5678_1 0x00 // 1 stop bit for all byte sizes
#define STOPBITS_5_1p5 0x04 // 1.5 stop bits for 5-bit byte
#define STOPBITS_678_2 0x04 // 2 stop bits for 6/7/8-bit byte
#define USA_PARITY_NONE 0x00
#define USA_PARITY_ODD 0x08
#define USA_PARITY_EVEN 0x18
#define PARITY_1 0x28
#define PARITY_0 0x38
// all things called "StatusMessage" are sent on the status endpoint
struct keyspan_usa26_portStatusMessage // one for each port
{
u8 port, // BOTH: 0=first, 1=second, other=see below
hskia_cts, // USA26: reports HSKIA pin
// USA17: reports CTS pin
gpia_dcd, // USA26: reports GPIA pin
// USA17: reports DCD pin
dsr, // USA17: reports DSR pin
ri, // USA17: reports RI pin
_txOff, // port has been disabled (by host)
_txXoff, // port is in XOFF state (either host or RX XOFF)
rxEnabled, // as configured by rxOn/rxOff 1=on, 0=off
controlResponse;// 1=a control message has been processed
};
// bits in RX data message when STAT byte is included
#define RXERROR_OVERRUN 0x02
#define RXERROR_PARITY 0x04
#define RXERROR_FRAMING 0x08
#define RXERROR_BREAK 0x10
struct keyspan_usa26_globalControlMessage
{
u8 sendGlobalStatus, // 2=request for two status responses
resetStatusToggle, // 1=reset global status toggle
resetStatusCount; // a cycling value
};
struct keyspan_usa26_globalStatusMessage
{
u8 port, // 3
sendGlobalStatus, // from request, decremented
resetStatusCount; // as in request
};
struct keyspan_usa26_globalDebugMessage
{
u8 port, // 2
a,
b,
c,
d;
};
// ie: the maximum length of an EZUSB endpoint buffer
#define MAX_DATA_LEN 64
// update status approx. 60 times a second (16.6666 ms)
#define STATUS_UPDATE_INTERVAL 16
// status rationing tuning value (each port gets checked each n ms)
#define STATUS_RATION 10
#endif

View file

@ -0,0 +1,201 @@
/*
usa28msg.h
Copyright (C) 1998-2000 InnoSys Incorporated. All Rights Reserved
This file is available under a BSD-style copyright
Keyspan USB Async Message Formats for the USA26X
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain this licence text
without modification, this list of conditions, and the following
disclaimer. The following copyright notice must appear immediately at
the beginning of all source files:
Copyright (C) 1998-2000 InnoSys Incorporated. All Rights Reserved
This file is available under a BSD-style copyright
2. The name of InnoSys Incorporated may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Note: these message formats are common to USA18, USA19, and USA28;
(for USA28X, see usa26msg.h)
Buffer formats for RX/TX data messages are not defined by
a structure, but are described here:
USB OUT (host -> USA28, transmit) messages contain a
REQUEST_ACK indicator (set to 0xff to request an ACK at the
completion of transmit; 0x00 otherwise), followed by data.
If the port is configured for parity, the data will be an
alternating string of parity and data bytes, so the message
format will be:
RQSTACK PAR DAT PAR DAT ...
so the maximum length is 63 bytes (1 + 62, or 31 data bytes);
always an odd number for the total message length.
If there is no parity, the format is simply:
RQSTACK DAT DAT DAT ...
with a total data length of 63.
USB IN (USA28 -> host, receive) messages contain data and parity
if parity is configred, thusly:
DAT PAR DAT PAR DAT PAR ...
for a total of 32 data bytes;
If parity is not configured, the format is:
DAT DAT DAT ...
for a total of 64 data bytes.
In the TX messages (USB OUT), the 0x01 bit of the PARity byte is
the parity bit. In the RX messages (USB IN), the PARity byte is
the content of the 8051's status register; the parity bit
(RX_PARITY_BIT) is the 0x04 bit.
revision history:
1999may06 add resetDataToggle to control message
2000mar21 add rs232invalid to status response message
2000apr04 add 230.4Kb definition to setBaudRate
2000apr13 add/remove loopbackMode switch
2000apr13 change definition of setBaudRate to cover 115.2Kb, too
2000jun01 add extended BSD-style copyright text
*/
#ifndef __USA28MSG__
#define __USA28MSG__
struct keyspan_usa28_portControlMessage
{
/*
there are four types of "commands" sent in the control message:
1. configuration changes which must be requested by setting
the corresponding "set" flag (and should only be requested
when necessary, to reduce overhead on the USA28):
*/
u8 setBaudRate, // 0=don't set, 1=baudLo/Hi, 2=115.2K, 3=230.4K
baudLo, // host does baud divisor calculation
baudHi; // baudHi is only used for first port (gives lower rates)
/*
2. configuration changes which are done every time (because it's
hardly more trouble to do them than to check whether to do them):
*/
u8 parity, // 1=use parity, 0=don't
ctsFlowControl, // all except 19Q: 1=use CTS flow control, 0=don't
// 19Q: 0x08:CTSflowControl 0x10:DSRflowControl
xonFlowControl, // 1=use XON/XOFF flow control, 0=don't
rts, // 1=on, 0=off
dtr; // 1=on, 0=off
/*
3. configuration data which is simply used as is (no overhead,
but must be correct in every host message).
*/
u8 forwardingLength, // forward when this number of chars available
forwardMs, // forward this many ms after last rx data
breakThreshold, // specified in ms, 1-255 (see note below)
xonChar, // specified in current character format
xoffChar; // specified in current character format
/*
4. commands which are flags only; these are processed in order
(so that, e.g., if both _txOn and _txOff flags are set, the
port ends in a TX_OFF state); any non-zero value is respected
*/
u8 _txOn, // enable transmitting (and continue if there's data)
_txOff, // stop transmitting
txFlush, // toss outbound data
txForceXoff, // pretend we've received XOFF
txBreak, // turn on break (leave on until txOn clears it)
rxOn, // turn on receiver
rxOff, // turn off receiver
rxFlush, // toss inbound data
rxForward, // forward all inbound data, NOW
returnStatus, // return current status n times (1 or 2)
resetDataToggle;// reset data toggle state to DATA0
};
struct keyspan_usa28_portStatusMessage
{
u8 port, // 0=first, 1=second, 2=global (see below)
cts,
dsr, // (not used in all products)
dcd,
ri, // (not used in all products)
_txOff, // port has been disabled (by host)
_txXoff, // port is in XOFF state (either host or RX XOFF)
dataLost, // count of lost chars; wraps; not guaranteed exact
rxEnabled, // as configured by rxOn/rxOff 1=on, 0=off
rxBreak, // 1=we're in break state
rs232invalid, // 1=no valid signals on rs-232 inputs
controlResponse;// 1=a control messages has been processed
};
// bit defines in txState
#define TX_OFF 0x01 // requested by host txOff command
#define TX_XOFF 0x02 // either real, or simulated by host
struct keyspan_usa28_globalControlMessage
{
u8 sendGlobalStatus, // 2=request for two status responses
resetStatusToggle, // 1=reset global status toggle
resetStatusCount; // a cycling value
};
struct keyspan_usa28_globalStatusMessage
{
u8 port, // 3
sendGlobalStatus, // from request, decremented
resetStatusCount; // as in request
};
struct keyspan_usa28_globalDebugMessage
{
u8 port, // 2
n, // typically a count/status byte
b; // typically a data byte
};
// ie: the maximum length of an EZUSB endpoint buffer
#define MAX_DATA_LEN 64
// the parity bytes have only one significant bit
#define RX_PARITY_BIT 0x04
#define TX_PARITY_BIT 0x01
// update status approx. 60 times a second (16.6666 ms)
#define STATUS_UPDATE_INTERVAL 16
#endif

View file

@ -0,0 +1,282 @@
/*
usa49msg.h
Copyright (C) 1998-2000 InnoSys Incorporated. All Rights Reserved
This file is available under a BSD-style copyright
Keyspan USB Async Message Formats for the USA49W
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain this licence text
without modification, this list of conditions, and the following
disclaimer. The following copyright notice must appear immediately at
the beginning of all source files:
Copyright (C) 1998-2000 InnoSys Incorporated. All Rights Reserved
This file is available under a BSD-style copyright
2. The name of InnoSys Incorporated may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
4th revision: USA49W version
Buffer formats for RX/TX data messages are not defined by
a structure, but are described here:
USB OUT (host -> USAxx, transmit) messages contain a
REQUEST_ACK indicator (set to 0xff to request an ACK at the
completion of transmit; 0x00 otherwise), followed by data:
RQSTACK DAT DAT DAT ...
with a total data length of 63.
USB IN (USAxx -> host, receive) messages begin with a status
byte in which the 0x80 bit is either:
(a) 0x80 bit clear
indicates that the bytes following it are all data
bytes:
STAT DATA DATA DATA DATA DATA ...
for a total of up to 63 DATA bytes,
or:
(b) 0x80 bit set
indiates that the bytes following alternate data and
status bytes:
STAT DATA STAT DATA STAT DATA STAT DATA ...
for a total of up to 32 DATA bytes.
The valid bits in the STAT bytes are:
OVERRUN 0x02
PARITY 0x04
FRAMING 0x08
BREAK 0x10
Notes:
(1) The OVERRUN bit can appear in either (a) or (b) format
messages, but the but the PARITY/FRAMING/BREAK bits
only appear in (b) format messages.
(2) For the host to determine the exact point at which the
overrun occurred (to identify the point in the data
stream at which the data was lost), it needs to count
128 characters, starting at the first character of the
message in which OVERRUN was reported; the lost character(s)
would have been received between the 128th and 129th
characters.
(3) An RX data message in which the first byte has 0x80 clear
serves as a "break off" indicator.
(4) a control message specifying disablePort will be answered
with a status message, but no further status will be sent
until a control messages with enablePort is sent
revision history:
1999feb10 add reportHskiaChanges to allow us to ignore them
1999feb10 add txAckThreshold for fast+loose throughput enhancement
1999mar30 beef up support for RX error reporting
1999apr14 add resetDataToggle to control message
2000jan04 merge with usa17msg.h
2000mar08 clone from usa26msg.h -> usa49msg.h
2000mar09 change to support 4 ports
2000may03 change external clocking to match USA-49W hardware
2000jun01 add extended BSD-style copyright text
2001jul05 change message format to improve OVERRUN case
*/
#ifndef __USA49MSG__
#define __USA49MSG__
/*
Host->device messages sent on the global control endpoint:
portNumber message
---------- --------------------
0,1,2,3 portControlMessage
0x80 globalControlMessage
*/
struct keyspan_usa49_portControlMessage
{
/*
0. 0/1/2/3 port control message follows
0x80 set non-port control message follows
*/
u8 portNumber,
/*
there are three types of "commands" sent in the control message:
1. configuration changes which must be requested by setting
the corresponding "set" flag (and should only be requested
when necessary, to reduce overhead on the USA26):
*/
setClocking, // host requests baud rate be set
baudLo, // host does baud divisor calculation
baudHi, // baudHi is only used for first port (gives lower rates)
prescaler, // specified as N/8; values 8-ff are valid
// must be set any time internal baud rate is set;
txClocking, // 0=internal, 1=external/DSR
rxClocking, // 0=internal, 1=external/DSR
setLcr, // host requests lcr be set
lcr, // use PARITY, STOPBITS, DATABITS below
setFlowControl, // host requests flow control be set
ctsFlowControl, // 1=use CTS flow control, 0=don't
xonFlowControl, // 1=use XON/XOFF flow control, 0=don't
xonChar, // specified in current character format
xoffChar, // specified in current character format
setRts, // host requests RTS output be set
rts, // 1=active, 0=inactive
setDtr, // host requests DTR output be set
dtr; // 1=on, 0=off
/*
3. configuration data which is simply used as is (no overhead,
but must be specified correctly in every host message).
*/
u8 forwardingLength, // forward when this number of chars available
dsrFlowControl, // 1=use DSR flow control, 0=don't
txAckThreshold, // 0=not allowed, 1=normal, 2-255 deliver ACK faster
loopbackMode; // 0=no loopback, 1=loopback enabled
/*
4. commands which are flags only; these are processed in order
(so that, e.g., if both _txOn and _txOff flags are set, the
port ends in a TX_OFF state); any non-zero value is respected
*/
u8 _txOn, // enable transmitting (and continue if there's data)
_txOff, // stop transmitting
txFlush, // toss outbound data
txBreak, // turn on break (cleared by _txOn)
rxOn, // turn on receiver
rxOff, // turn off receiver
rxFlush, // toss inbound data
rxForward, // forward all inbound data, NOW (as if fwdLen==1)
returnStatus, // return current status (even if it hasn't changed)
resetDataToggle,// reset data toggle state to DATA0
enablePort, // start servicing port (move data, check status)
disablePort; // stop servicing port (does implicit tx/rx flush/off)
};
// defines for bits in lcr
#define USA_DATABITS_5 0x00
#define USA_DATABITS_6 0x01
#define USA_DATABITS_7 0x02
#define USA_DATABITS_8 0x03
#define STOPBITS_5678_1 0x00 // 1 stop bit for all byte sizes
#define STOPBITS_5_1p5 0x04 // 1.5 stop bits for 5-bit byte
#define STOPBITS_678_2 0x04 // 2 stop bits for 6/7/8-bit byte
#define USA_PARITY_NONE 0x00
#define USA_PARITY_ODD 0x08
#define USA_PARITY_EVEN 0x18
#define PARITY_1 0x28
#define PARITY_0 0x38
/*
during normal operation, status messages are returned
to the host whenever the board detects changes. In some
circumstances (e.g. Windows), status messages from the
device cause problems; to shut them off, the host issues
a control message with the disableStatusMessages flags
set (to any non-zero value). The device will respond to
this message, and then suppress further status messages;
it will resume sending status messages any time the host
sends any control message (either global or port-specific).
*/
struct keyspan_usa49_globalControlMessage
{
u8 portNumber, // 0x80
sendGlobalStatus, // 1/2=number of status responses requested
resetStatusToggle, // 1=reset global status toggle
resetStatusCount, // a cycling value
remoteWakeupEnable, // 0x10=P1, 0x20=P2, 0x40=P3, 0x80=P4
disableStatusMessages; // 1=send no status until host talks
};
/*
Device->host messages send on the global status endpoint
portNumber message
---------- --------------------
0x00,0x01,0x02,0x03 portStatusMessage
0x80 globalStatusMessage
0x81 globalDebugMessage
*/
struct keyspan_usa49_portStatusMessage // one for each port
{
u8 portNumber, // 0,1,2,3
cts, // reports CTS pin
dcd, // reports DCD pin
dsr, // reports DSR pin
ri, // reports RI pin
_txOff, // transmit has been disabled (by host)
_txXoff, // transmit is in XOFF state (either host or RX XOFF)
rxEnabled, // as configured by rxOn/rxOff 1=on, 0=off
controlResponse,// 1=a control message has been processed
txAck, // ACK (data TX complete)
rs232valid; // RS-232 signal valid
};
// bits in RX data message when STAT byte is included
#define RXERROR_OVERRUN 0x02
#define RXERROR_PARITY 0x04
#define RXERROR_FRAMING 0x08
#define RXERROR_BREAK 0x10
struct keyspan_usa49_globalStatusMessage
{
u8 portNumber, // 0x80=globalStatusMessage
sendGlobalStatus, // from request, decremented
resetStatusCount; // as in request
};
struct keyspan_usa49_globalDebugMessage
{
u8 portNumber, // 0x81=globalDebugMessage
n, // typically a count/status byte
b; // typically a data byte
};
// ie: the maximum length of an EZUSB endpoint buffer
#define MAX_DATA_LEN 64
// update status approx. 60 times a second (16.6666 ms)
#define STATUS_UPDATE_INTERVAL 16
// status rationing tuning value (each port gets checked each n ms)
#define STATUS_RATION 10
#endif

View file

@ -0,0 +1,254 @@
/*
usa67msg.h
Copyright (c) 1998-2007 InnoSys Incorporated. All Rights Reserved
This file is available under a BSD-style copyright
Keyspan USB Async Firmware to run on Anchor FX1
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain this licence text
without modification, this list of conditions, and the following
disclaimer. The following copyright notice must appear immediately at
the beginning of all source files:
Copyright (c) 1998-2007 InnoSys Incorporated. All Rights Reserved
This file is available under a BSD-style copyright
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of InnoSys Incorprated may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Fourth revision: This message format supports the USA28XG
Buffer formats for RX/TX data messages are not defined by
a structure, but are described here:
USB OUT (host -> USAxx, transmit) messages contain a
REQUEST_ACK indicator (set to 0xff to request an ACK at the
completion of transmit; 0x00 otherwise), followed by data:
RQSTACK DAT DAT DAT ...
with a total data length of up to 63.
USB IN (USAxx -> host, receive) messages begin with a status
byte in which the 0x80 bit is either:
(a) 0x80 bit clear
indicates that the bytes following it are all data
bytes:
STAT DATA DATA DATA DATA DATA ...
for a total of up to 63 DATA bytes,
or:
(b) 0x80 bit set
indiates that the bytes following alternate data and
status bytes:
STAT DATA STAT DATA STAT DATA STAT DATA ...
for a total of up to 32 DATA bytes.
The valid bits in the STAT bytes are:
OVERRUN 0x02
PARITY 0x04
FRAMING 0x08
BREAK 0x10
Notes:
(1) The OVERRUN bit can appear in either (a) or (b) format
messages, but the but the PARITY/FRAMING/BREAK bits
only appear in (b) format messages.
(2) For the host to determine the exact point at which the
overrun occurred (to identify the point in the data
stream at which the data was lost), it needs to count
128 characters, starting at the first character of the
message in which OVERRUN was reported; the lost character(s)
would have been received between the 128th and 129th
characters.
(3) An RX data message in which the first byte has 0x80 clear
serves as a "break off" indicator.
revision history:
1999feb10 add reportHskiaChanges to allow us to ignore them
1999feb10 add txAckThreshold for fast+loose throughput enhancement
1999mar30 beef up support for RX error reporting
1999apr14 add resetDataToggle to control message
2000jan04 merge with usa17msg.h
2000jun01 add extended BSD-style copyright text
2001jul05 change message format to improve OVERRUN case
2002jun05 update copyright date, improve comments
2006feb06 modify for FX1 chip
*/
#ifndef __USA67MSG__
#define __USA67MSG__
// all things called "ControlMessage" are sent on the 'control' endpoint
typedef struct keyspan_usa67_portControlMessage
{
u8 port; // 0 or 1 (selects port)
/*
there are three types of "commands" sent in the control message:
1. configuration changes which must be requested by setting
the corresponding "set" flag (and should only be requested
when necessary, to reduce overhead on the device):
*/
u8 setClocking, // host requests baud rate be set
baudLo, // host does baud divisor calculation
baudHi, // baudHi is only used for first port (gives lower rates)
externalClock_txClocking,
// 0=internal, other=external
setLcr, // host requests lcr be set
lcr, // use PARITY, STOPBITS, DATABITS below
setFlowControl, // host requests flow control be set
ctsFlowControl, // 1=use CTS flow control, 0=don't
xonFlowControl, // 1=use XON/XOFF flow control, 0=don't
xonChar, // specified in current character format
xoffChar, // specified in current character format
setTxTriState_setRts,
// host requests TX tri-state be set
txTriState_rts, // 1=active (normal), 0=tristate (off)
setHskoa_setDtr,
// host requests HSKOA output be set
hskoa_dtr, // 1=on, 0=off
setPrescaler, // host requests prescalar be set (default: 13)
prescaler; // specified as N/8; values 8-ff are valid
// must be set any time internal baud rate is set;
// must not be set when external clocking is used
/*
3. configuration data which is simply used as is (no overhead,
but must be specified correctly in every host message).
*/
u8 forwardingLength, // forward when this number of chars available
reportHskiaChanges_dsrFlowControl,
// 1=normal; 0=ignore external clock
// 1=use DSR flow control, 0=don't
txAckThreshold, // 0=not allowed, 1=normal, 2-255 deliver ACK faster
loopbackMode; // 0=no loopback, 1=loopback enabled
/*
4. commands which are flags only; these are processed in order
(so that, e.g., if both _txOn and _txOff flags are set, the
port ends in a TX_OFF state); any non-zero value is respected
*/
u8 _txOn, // enable transmitting (and continue if there's data)
_txOff, // stop transmitting
txFlush, // toss outbound data
txBreak, // turn on break (cleared by _txOn)
rxOn, // turn on receiver
rxOff, // turn off receiver
rxFlush, // toss inbound data
rxForward, // forward all inbound data, NOW (as if fwdLen==1)
returnStatus, // return current status (even if it hasn't changed)
resetDataToggle;// reset data toggle state to DATA0
} keyspan_usa67_portControlMessage;
// defines for bits in lcr
#define USA_DATABITS_5 0x00
#define USA_DATABITS_6 0x01
#define USA_DATABITS_7 0x02
#define USA_DATABITS_8 0x03
#define STOPBITS_5678_1 0x00 // 1 stop bit for all byte sizes
#define STOPBITS_5_1p5 0x04 // 1.5 stop bits for 5-bit byte
#define STOPBITS_678_2 0x04 // 2 stop bits for 6/7/8-bit byte
#define USA_PARITY_NONE 0x00
#define USA_PARITY_ODD 0x08
#define USA_PARITY_EVEN 0x18
#define PARITY_1 0x28
#define PARITY_0 0x38
// all things called "StatusMessage" are sent on the status endpoint
typedef struct keyspan_usa67_portStatusMessage // one for each port
{
u8 port, // 0=first, 1=second, other=see below
hskia_cts, // reports HSKIA pin
gpia_dcd, // reports GPIA pin
_txOff, // port has been disabled (by host)
_txXoff, // port is in XOFF state (either host or RX XOFF)
txAck, // indicates a TX message acknowledgement
rxEnabled, // as configured by rxOn/rxOff 1=on, 0=off
controlResponse;// 1=a control message has been processed
} keyspan_usa67_portStatusMessage;
// bits in RX data message when STAT byte is included
#define RXERROR_OVERRUN 0x02
#define RXERROR_PARITY 0x04
#define RXERROR_FRAMING 0x08
#define RXERROR_BREAK 0x10
typedef struct keyspan_usa67_globalControlMessage
{
u8 port, // 3
sendGlobalStatus, // 2=request for two status responses
resetStatusToggle, // 1=reset global status toggle
resetStatusCount; // a cycling value
} keyspan_usa67_globalControlMessage;
typedef struct keyspan_usa67_globalStatusMessage
{
u8 port, // 3
sendGlobalStatus, // from request, decremented
resetStatusCount; // as in request
} keyspan_usa67_globalStatusMessage;
typedef struct keyspan_usa67_globalDebugMessage
{
u8 port, // 2
a,
b,
c,
d;
} keyspan_usa67_globalDebugMessage;
// ie: the maximum length of an FX1 endpoint buffer
#define MAX_DATA_LEN 64
// update status approx. 60 times a second (16.6666 ms)
#define STATUS_UPDATE_INTERVAL 16
// status rationing tuning value (each port gets checked each n ms)
#define STATUS_RATION 10
#endif

View file

@ -0,0 +1,198 @@
/*
usa90msg.h
Copyright (c) 1998-2003 InnoSys Incorporated. All Rights Reserved
This file is available under a BSD-style copyright
Keyspan USB Async Message Formats for the USA19HS
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain this licence text
without modification, this list of conditions, and the following
disclaimer. The following copyright notice must appear immediately at
the beginning of all source files:
Copyright (c) 1998-2003 InnoSys Incorporated. All Rights Reserved
This file is available under a BSD-style copyright
2. The name of InnoSys Incorporated may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Revisions:
2003feb14 add setTxMode/txMode and cancelRxXoff to portControl
2003mar21 change name of PARITY_0/1 to add MARK/SPACE
*/
#ifndef __USA90MSG__
#define __USA90MSG__
struct keyspan_usa90_portControlMessage
{
/*
there are three types of "commands" sent in the control message:
1. configuration changes which must be requested by setting
the corresponding "set" flag (and should only be requested
when necessary, to reduce overhead on the device):
*/
u8 setClocking, // host requests baud rate be set
baudLo, // host does baud divisor calculation
baudHi, // host does baud divisor calculation
setLcr, // host requests lcr be set
lcr, // use PARITY, STOPBITS, DATABITS below
setRxMode, // set receive mode
rxMode, // RXMODE_DMA or RXMODE_BYHAND
setTxMode, // set transmit mode
txMode, // TXMODE_DMA or TXMODE_BYHAND
setTxFlowControl, // host requests tx flow control be set
txFlowControl , // use TX_FLOW... bits below
setRxFlowControl, // host requests rx flow control be set
rxFlowControl, // use RX_FLOW... bits below
sendXoff, // host requests XOFF transmitted immediately
sendXon, // host requests XON char transmitted
xonChar, // specified in current character format
xoffChar, // specified in current character format
sendChar, // host requests char transmitted immediately
txChar, // character to send
setRts, // host requests RTS output be set
rts, // 1=on, 0=off
setDtr, // host requests DTR output be set
dtr; // 1=on, 0=off
/*
2. configuration data which is simply used as is
and must be specified correctly in every host message.
*/
u8 rxForwardingLength, // forward when this number of chars available
rxForwardingTimeout, // (1-31 in ms)
txAckSetting; // 0=don't ack, 1=normal, 2-255 TBD...
/*
3. Firmware states which cause actions if they change
and must be specified correctly in every host message.
*/
u8 portEnabled, // 0=disabled, 1=enabled
txFlush, // 0=normal, 1=toss outbound data
txBreak, // 0=break off, 1=break on
loopbackMode; // 0=no loopback, 1=loopback enabled
/*
4. commands which are flags only; these are processed in order
(so that, e.g., if rxFlush and rxForward flags are set, the
port will have no data to forward); any non-zero value
is respected
*/
u8 rxFlush, // toss inbound data
rxForward, // forward all inbound data, NOW (as if fwdLen==1)
cancelRxXoff, // cancel any receive XOFF state (_txXoff)
returnStatus; // return current status NOW
};
// defines for bits in lcr
#define USA_DATABITS_5 0x00
#define USA_DATABITS_6 0x01
#define USA_DATABITS_7 0x02
#define USA_DATABITS_8 0x03
#define STOPBITS_5678_1 0x00 // 1 stop bit for all byte sizes
#define STOPBITS_5_1p5 0x04 // 1.5 stop bits for 5-bit byte
#define STOPBITS_678_2 0x04 // 2 stop bits for 6-8 bit byte
#define USA_PARITY_NONE 0x00
#define USA_PARITY_ODD 0x08
#define USA_PARITY_EVEN 0x18
#define PARITY_MARK_1 0x28 // force parity MARK
#define PARITY_SPACE_0 0x38 // force parity SPACE
#define TXFLOW_CTS 0x04
#define TXFLOW_DSR 0x08
#define TXFLOW_XOFF 0x01
#define TXFLOW_XOFF_ANY 0x02
#define TXFLOW_XOFF_BITS (TXFLOW_XOFF | TXFLOW_XOFF_ANY)
#define RXFLOW_XOFF 0x10
#define RXFLOW_RTS 0x20
#define RXFLOW_DTR 0x40
#define RXFLOW_DSR_SENSITIVITY 0x80
#define RXMODE_BYHAND 0x00
#define RXMODE_DMA 0x02
#define TXMODE_BYHAND 0x00
#define TXMODE_DMA 0x02
// all things called "StatusMessage" are sent on the status endpoint
struct keyspan_usa90_portStatusMessage
{
u8 msr, // reports the actual MSR register
cts, // reports CTS pin
dcd, // reports DCD pin
dsr, // reports DSR pin
ri, // reports RI pin
_txXoff, // port is in XOFF state (we received XOFF)
rxBreak, // reports break state
rxOverrun, // count of overrun errors (since last reported)
rxParity, // count of parity errors (since last reported)
rxFrame, // count of frame errors (since last reported)
portState, // PORTSTATE_xxx bits (useful for debugging)
messageAck, // message acknowledgement
charAck, // character acknowledgement
controlResponse; // (value = returnStatus) a control message has been processed
};
// bits in RX data message when STAT byte is included
#define RXERROR_OVERRUN 0x02
#define RXERROR_PARITY 0x04
#define RXERROR_FRAMING 0x08
#define RXERROR_BREAK 0x10
#define PORTSTATE_ENABLED 0x80
#define PORTSTATE_TXFLUSH 0x01
#define PORTSTATE_TXBREAK 0x02
#define PORTSTATE_LOOPBACK 0x04
// MSR bits
#define USA_MSR_dCTS 0x01 // CTS has changed since last report
#define USA_MSR_dDSR 0x02
#define USA_MSR_dRI 0x04
#define USA_MSR_dDCD 0x08
#define USA_MSR_CTS 0x10 // current state of CTS
#define USA_MSR_DSR 0x20
#define USA_USA_MSR_RI 0x40
#define MSR_DCD 0x80
// ie: the maximum length of an endpoint buffer
#define MAX_DATA_LEN 64
#endif

View file

@ -0,0 +1,606 @@
/*
* KLSI KL5KUSB105 chip RS232 converter driver
*
* Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
* Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* All information about the device was acquired using SniffUSB ans snoopUSB
* on Windows98.
* It was written out of frustration with the PalmConnect USB Serial adapter
* sold by Palm Inc.
* Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided
* information that was not already available.
*
* It seems that KLSI bought some silicon-design information from ScanLogic,
* whose SL11R processor is at the core of the KL5KUSB chipset from KLSI.
* KLSI has firmware available for their devices; it is probable that the
* firmware differs from that used by KLSI in their products. If you have an
* original KLSI device and can provide some information on it, I would be
* most interested in adding support for it here. If you have any information
* on the protocol used (or find errors in my reverse-engineered stuff), please
* let me know.
*
* The code was only tested with a PalmConnect USB adapter; if you
* are adventurous, try it with any KLSI-based device and let me know how it
* breaks so that I can fix it!
*/
/* TODO:
* check modem line signals
* implement handshaking or decide that we do not support it
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include "kl5kusb105.h"
#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>, Johan Hovold <jhovold@gmail.com>"
#define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"
/*
* Function prototypes
*/
static int klsi_105_port_probe(struct usb_serial_port *port);
static int klsi_105_port_remove(struct usb_serial_port *port);
static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port);
static void klsi_105_close(struct usb_serial_port *port);
static void klsi_105_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old);
static int klsi_105_tiocmget(struct tty_struct *tty);
static void klsi_105_process_read_urb(struct urb *urb);
static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
void *dest, size_t size);
/*
* All of the device info needed for the KLSI converters.
*/
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
{ USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver kl5kusb105d_device = {
.driver = {
.owner = THIS_MODULE,
.name = "kl5kusb105d",
},
.description = "KL5KUSB105D / PalmConnect",
.id_table = id_table,
.num_ports = 1,
.bulk_out_size = 64,
.open = klsi_105_open,
.close = klsi_105_close,
.set_termios = klsi_105_set_termios,
/*.break_ctl = klsi_105_break_ctl,*/
.tiocmget = klsi_105_tiocmget,
.port_probe = klsi_105_port_probe,
.port_remove = klsi_105_port_remove,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
.process_read_urb = klsi_105_process_read_urb,
.prepare_write_buffer = klsi_105_prepare_write_buffer,
};
static struct usb_serial_driver * const serial_drivers[] = {
&kl5kusb105d_device, NULL
};
struct klsi_105_port_settings {
__u8 pktlen; /* always 5, it seems */
__u8 baudrate;
__u8 databits;
__u8 unknown1;
__u8 unknown2;
} __attribute__ ((packed));
struct klsi_105_private {
struct klsi_105_port_settings cfg;
struct ktermios termios;
unsigned long line_state; /* modem line settings */
spinlock_t lock;
};
/*
* Handle vendor specific USB requests
*/
#define KLSI_TIMEOUT 5000 /* default urb timeout */
static int klsi_105_chg_port_settings(struct usb_serial_port *port,
struct klsi_105_port_settings *settings)
{
int rc;
rc = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
KL5KUSB105A_SIO_SET_DATA,
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE,
0, /* value */
0, /* index */
settings,
sizeof(struct klsi_105_port_settings),
KLSI_TIMEOUT);
if (rc < 0)
dev_err(&port->dev,
"Change port settings failed (error = %d)\n", rc);
dev_info(&port->serial->dev->dev,
"%d byte block, baudrate %x, databits %d, u1 %d, u2 %d\n",
settings->pktlen, settings->baudrate, settings->databits,
settings->unknown1, settings->unknown2);
return rc;
}
/* translate a 16-bit status value from the device to linux's TIO bits */
static unsigned long klsi_105_status2linestate(const __u16 status)
{
unsigned long res = 0;
res = ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0)
| ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0)
;
return res;
}
/*
* Read line control via vendor command and return result through
* *line_state_p
*/
/* It seems that the status buffer has always only 2 bytes length */
#define KLSI_STATUSBUF_LEN 2
static int klsi_105_get_line_state(struct usb_serial_port *port,
unsigned long *line_state_p)
{
int rc;
u8 *status_buf;
__u16 status;
dev_info(&port->serial->dev->dev, "sending SIO Poll request\n");
status_buf = kmalloc(KLSI_STATUSBUF_LEN, GFP_KERNEL);
if (!status_buf)
return -ENOMEM;
status_buf[0] = 0xff;
status_buf[1] = 0xff;
rc = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
KL5KUSB105A_SIO_POLL,
USB_TYPE_VENDOR | USB_DIR_IN,
0, /* value */
0, /* index */
status_buf, KLSI_STATUSBUF_LEN,
10000
);
if (rc < 0)
dev_err(&port->dev, "Reading line status failed (error = %d)\n",
rc);
else {
status = get_unaligned_le16(status_buf);
dev_info(&port->serial->dev->dev, "read status %x %x\n",
status_buf[0], status_buf[1]);
*line_state_p = klsi_105_status2linestate(status);
}
kfree(status_buf);
return rc;
}
/*
* Driver's tty interface functions
*/
static int klsi_105_port_probe(struct usb_serial_port *port)
{
struct klsi_105_private *priv;
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* set initial values for control structures */
priv->cfg.pktlen = 5;
priv->cfg.baudrate = kl5kusb105a_sio_b9600;
priv->cfg.databits = kl5kusb105a_dtb_8;
priv->cfg.unknown1 = 0;
priv->cfg.unknown2 = 1;
priv->line_state = 0;
spin_lock_init(&priv->lock);
/* priv->termios is left uninitialized until port opening */
usb_set_serial_port_data(port, priv);
return 0;
}
static int klsi_105_port_remove(struct usb_serial_port *port)
{
struct klsi_105_private *priv;
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct klsi_105_private *priv = usb_get_serial_port_data(port);
int retval = 0;
int rc;
int i;
unsigned long line_state;
struct klsi_105_port_settings *cfg;
unsigned long flags;
/* Do a defined restart:
* Set up sane default baud rate and send the 'READ_ON'
* vendor command.
* FIXME: set modem line control (how?)
* Then read the modem line control and store values in
* priv->line_state.
*/
cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
cfg->pktlen = 5;
cfg->baudrate = kl5kusb105a_sio_b9600;
cfg->databits = kl5kusb105a_dtb_8;
cfg->unknown1 = 0;
cfg->unknown2 = 1;
klsi_105_chg_port_settings(port, cfg);
/* set up termios structure */
spin_lock_irqsave(&priv->lock, flags);
priv->termios.c_iflag = tty->termios.c_iflag;
priv->termios.c_oflag = tty->termios.c_oflag;
priv->termios.c_cflag = tty->termios.c_cflag;
priv->termios.c_lflag = tty->termios.c_lflag;
for (i = 0; i < NCCS; i++)
priv->termios.c_cc[i] = tty->termios.c_cc[i];
priv->cfg.pktlen = cfg->pktlen;
priv->cfg.baudrate = cfg->baudrate;
priv->cfg.databits = cfg->databits;
priv->cfg.unknown1 = cfg->unknown1;
priv->cfg.unknown2 = cfg->unknown2;
spin_unlock_irqrestore(&priv->lock, flags);
/* READ_ON and urb submission */
rc = usb_serial_generic_open(tty, port);
if (rc) {
retval = rc;
goto exit;
}
rc = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
KL5KUSB105A_SIO_CONFIGURE,
USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE,
KL5KUSB105A_SIO_CONFIGURE_READ_ON,
0, /* index */
NULL,
0,
KLSI_TIMEOUT);
if (rc < 0) {
dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);
retval = rc;
} else
dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
rc = klsi_105_get_line_state(port, &line_state);
if (rc >= 0) {
spin_lock_irqsave(&priv->lock, flags);
priv->line_state = line_state;
spin_unlock_irqrestore(&priv->lock, flags);
dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
retval = 0;
} else
retval = rc;
exit:
kfree(cfg);
return retval;
}
static void klsi_105_close(struct usb_serial_port *port)
{
int rc;
/* send READ_OFF */
rc = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
KL5KUSB105A_SIO_CONFIGURE,
USB_TYPE_VENDOR | USB_DIR_OUT,
KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
0, /* index */
NULL, 0,
KLSI_TIMEOUT);
if (rc < 0)
dev_err(&port->dev, "failed to disable read: %d\n", rc);
/* shutdown our bulk reads and writes */
usb_serial_generic_close(port);
}
/* We need to write a complete 64-byte data block and encode the
* number actually sent in the first double-byte, LSB-order. That
* leaves at most 62 bytes of payload.
*/
#define KLSI_HDR_LEN 2
static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
void *dest, size_t size)
{
unsigned char *buf = dest;
int count;
count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size,
&port->lock);
put_unaligned_le16(count, buf);
return count + KLSI_HDR_LEN;
}
/* The data received is preceded by a length double-byte in LSB-first order.
*/
static void klsi_105_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
unsigned len;
/* empty urbs seem to happen, we ignore them */
if (!urb->actual_length)
return;
if (urb->actual_length <= KLSI_HDR_LEN) {
dev_dbg(&port->dev, "%s - malformed packet\n", __func__);
return;
}
len = get_unaligned_le16(data);
if (len > urb->actual_length - KLSI_HDR_LEN) {
dev_dbg(&port->dev, "%s - packet length mismatch\n", __func__);
len = urb->actual_length - KLSI_HDR_LEN;
}
tty_insert_flip_string(&port->port, data + KLSI_HDR_LEN, len);
tty_flip_buffer_push(&port->port);
}
static void klsi_105_set_termios(struct tty_struct *tty,
struct usb_serial_port *port,
struct ktermios *old_termios)
{
struct klsi_105_private *priv = usb_get_serial_port_data(port);
struct device *dev = &port->dev;
unsigned int iflag = tty->termios.c_iflag;
unsigned int old_iflag = old_termios->c_iflag;
unsigned int cflag = tty->termios.c_cflag;
unsigned int old_cflag = old_termios->c_cflag;
struct klsi_105_port_settings *cfg;
unsigned long flags;
speed_t baud;
cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return;
/* lock while we are modifying the settings */
spin_lock_irqsave(&priv->lock, flags);
/*
* Update baud rate
*/
baud = tty_get_baud_rate(tty);
if ((cflag & CBAUD) != (old_cflag & CBAUD)) {
/* reassert DTR and (maybe) RTS on transition from B0 */
if ((old_cflag & CBAUD) == B0) {
dev_dbg(dev, "%s: baud was B0\n", __func__);
#if 0
priv->control_state |= TIOCM_DTR;
/* don't set RTS if using hardware flow control */
if (!(old_cflag & CRTSCTS))
priv->control_state |= TIOCM_RTS;
mct_u232_set_modem_ctrl(serial, priv->control_state);
#endif
}
}
switch (baud) {
case 0: /* handled below */
break;
case 1200:
priv->cfg.baudrate = kl5kusb105a_sio_b1200;
break;
case 2400:
priv->cfg.baudrate = kl5kusb105a_sio_b2400;
break;
case 4800:
priv->cfg.baudrate = kl5kusb105a_sio_b4800;
break;
case 9600:
priv->cfg.baudrate = kl5kusb105a_sio_b9600;
break;
case 19200:
priv->cfg.baudrate = kl5kusb105a_sio_b19200;
break;
case 38400:
priv->cfg.baudrate = kl5kusb105a_sio_b38400;
break;
case 57600:
priv->cfg.baudrate = kl5kusb105a_sio_b57600;
break;
case 115200:
priv->cfg.baudrate = kl5kusb105a_sio_b115200;
break;
default:
dev_dbg(dev, "unsupported baudrate, using 9600\n");
priv->cfg.baudrate = kl5kusb105a_sio_b9600;
baud = 9600;
break;
}
if ((cflag & CBAUD) == B0) {
dev_dbg(dev, "%s: baud is B0\n", __func__);
/* Drop RTS and DTR */
/* maybe this should be simulated by sending read
* disable and read enable messages?
*/
;
#if 0
priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
mct_u232_set_modem_ctrl(serial, priv->control_state);
#endif
}
tty_encode_baud_rate(tty, baud, baud);
if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
/* set the number of data bits */
switch (cflag & CSIZE) {
case CS5:
dev_dbg(dev, "%s - 5 bits/byte not supported\n", __func__);
spin_unlock_irqrestore(&priv->lock, flags);
goto err;
case CS6:
dev_dbg(dev, "%s - 6 bits/byte not supported\n", __func__);
spin_unlock_irqrestore(&priv->lock, flags);
goto err;
case CS7:
priv->cfg.databits = kl5kusb105a_dtb_7;
break;
case CS8:
priv->cfg.databits = kl5kusb105a_dtb_8;
break;
default:
dev_err(dev, "CSIZE was not CS5-CS8, using default of 8\n");
priv->cfg.databits = kl5kusb105a_dtb_8;
break;
}
}
/*
* Update line control register (LCR)
*/
if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))
|| (cflag & CSTOPB) != (old_cflag & CSTOPB)) {
/* Not currently supported */
tty->termios.c_cflag &= ~(PARENB|PARODD|CSTOPB);
#if 0
priv->last_lcr = 0;
/* set the parity */
if (cflag & PARENB)
priv->last_lcr |= (cflag & PARODD) ?
MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
else
priv->last_lcr |= MCT_U232_PARITY_NONE;
/* set the number of stop bits */
priv->last_lcr |= (cflag & CSTOPB) ?
MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
mct_u232_set_line_ctrl(serial, priv->last_lcr);
#endif
;
}
/*
* Set flow control: well, I do not really now how to handle DTR/RTS.
* Just do what we have seen with SniffUSB on Win98.
*/
if ((iflag & IXOFF) != (old_iflag & IXOFF)
|| (iflag & IXON) != (old_iflag & IXON)
|| (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
/* Not currently supported */
tty->termios.c_cflag &= ~CRTSCTS;
/* Drop DTR/RTS if no flow control otherwise assert */
#if 0
if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS))
priv->control_state |= TIOCM_DTR | TIOCM_RTS;
else
priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
mct_u232_set_modem_ctrl(serial, priv->control_state);
#endif
;
}
memcpy(cfg, &priv->cfg, sizeof(*cfg));
spin_unlock_irqrestore(&priv->lock, flags);
/* now commit changes to device */
klsi_105_chg_port_settings(port, cfg);
err:
kfree(cfg);
}
#if 0
static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
struct mct_u232_private *priv =
(struct mct_u232_private *)port->private;
unsigned char lcr = priv->last_lcr;
dev_dbg(&port->dev, "%s - state=%d\n", __func__, break_state);
/* LOCKING */
if (break_state)
lcr |= MCT_U232_SET_BREAK;
mct_u232_set_line_ctrl(serial, lcr);
}
#endif
static int klsi_105_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct klsi_105_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
int rc;
unsigned long line_state;
rc = klsi_105_get_line_state(port, &line_state);
if (rc < 0) {
dev_err(&port->dev,
"Reading line control failed (error = %d)\n", rc);
/* better return value? EAGAIN? */
return rc;
}
spin_lock_irqsave(&priv->lock, flags);
priv->line_state = line_state;
spin_unlock_irqrestore(&priv->lock, flags);
dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
return (int)line_state;
}
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,68 @@
/*
* Definitions for the KLSI KL5KUSB105 serial port adapter
*/
/* vendor/product pairs that are known to contain this chipset */
#define PALMCONNECT_VID 0x0830
#define PALMCONNECT_PID 0x0080
#define KLSI_VID 0x05e9
#define KLSI_KL5KUSB105D_PID 0x00c0
/* Vendor commands: */
/* port table -- the chip supports up to 4 channels */
/* baud rates */
enum {
kl5kusb105a_sio_b115200 = 0,
kl5kusb105a_sio_b57600 = 1,
kl5kusb105a_sio_b38400 = 2,
kl5kusb105a_sio_b19200 = 4,
kl5kusb105a_sio_b14400 = 5,
kl5kusb105a_sio_b9600 = 6,
kl5kusb105a_sio_b4800 = 8, /* unchecked */
kl5kusb105a_sio_b2400 = 9, /* unchecked */
kl5kusb105a_sio_b1200 = 0xa, /* unchecked */
kl5kusb105a_sio_b600 = 0xb /* unchecked */
};
/* data bits */
#define kl5kusb105a_dtb_7 7
#define kl5kusb105a_dtb_8 8
/* requests: */
#define KL5KUSB105A_SIO_SET_DATA 1
#define KL5KUSB105A_SIO_POLL 2
#define KL5KUSB105A_SIO_CONFIGURE 3
/* values used for request KL5KUSB105A_SIO_CONFIGURE */
#define KL5KUSB105A_SIO_CONFIGURE_READ_ON 3
#define KL5KUSB105A_SIO_CONFIGURE_READ_OFF 2
/* Interpretation of modem status lines */
/* These need sorting out by individually connecting pins and checking
* results. FIXME!
* When data is being sent we see 0x30 in the lower byte; this must
* contain DSR and CTS ...
*/
#define KL5KUSB105A_DSR ((1<<4) | (1<<5))
#define KL5KUSB105A_CTS ((1<<5) | (1<<4))
#define KL5KUSB105A_WANTS_TO_SEND 0x30
#if 0
#define KL5KUSB105A_DTR /* Data Terminal Ready */
#define KL5KUSB105A_CTS /* Clear To Send */
#define KL5KUSB105A_CD /* Carrier Detect */
#define KL5KUSB105A_DSR /* Data Set Ready */
#define KL5KUSB105A_RxD /* Receive pin */
#define KL5KUSB105A_LE
#define KL5KUSB105A_RTS
#define KL5KUSB105A_ST
#define KL5KUSB105A_SR
#define KL5KUSB105A_RI /* Ring Indicator */
#endif

View file

@ -0,0 +1,559 @@
/*
* KOBIL USB Smart Card Terminal Driver
*
* Copyright (C) 2002 KOBIL Systems GmbH
* Author: Thomas Wahrenbruch
*
* Contact: linuxusb@kobil.de
*
* This program is largely derived from work by the linux-usb group
* and associated source files. Please see the usb/serial files for
* individual credits and copyrights.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Thanks to Greg Kroah-Hartman (greg@kroah.com) for his help and
* patience.
*
* Supported readers: USB TWIN, KAAN Standard Plus and SecOVID Reader Plus
* (Adapter K), B1 Professional and KAAN Professional (Adapter B)
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/ioctl.h>
#include "kobil_sct.h"
#define DRIVER_AUTHOR "KOBIL Systems GmbH - http://www.kobil.com"
#define DRIVER_DESC "KOBIL USB Smart Card Terminal Driver (experimental)"
#define KOBIL_VENDOR_ID 0x0D46
#define KOBIL_ADAPTER_B_PRODUCT_ID 0x2011
#define KOBIL_ADAPTER_K_PRODUCT_ID 0x2012
#define KOBIL_USBTWIN_PRODUCT_ID 0x0078
#define KOBIL_KAAN_SIM_PRODUCT_ID 0x0081
#define KOBIL_TIMEOUT 500
#define KOBIL_BUF_LENGTH 300
/* Function prototypes */
static int kobil_port_probe(struct usb_serial_port *probe);
static int kobil_port_remove(struct usb_serial_port *probe);
static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port);
static void kobil_close(struct usb_serial_port *port);
static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static int kobil_write_room(struct tty_struct *tty);
static int kobil_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
static int kobil_tiocmget(struct tty_struct *tty);
static int kobil_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
static void kobil_read_int_callback(struct urb *urb);
static void kobil_write_int_callback(struct urb *urb);
static void kobil_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old);
static void kobil_init_termios(struct tty_struct *tty);
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_B_PRODUCT_ID) },
{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_K_PRODUCT_ID) },
{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_USBTWIN_PRODUCT_ID) },
{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_KAAN_SIM_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver kobil_device = {
.driver = {
.owner = THIS_MODULE,
.name = "kobil",
},
.description = "KOBIL USB smart card terminal",
.id_table = id_table,
.num_ports = 1,
.port_probe = kobil_port_probe,
.port_remove = kobil_port_remove,
.ioctl = kobil_ioctl,
.set_termios = kobil_set_termios,
.init_termios = kobil_init_termios,
.tiocmget = kobil_tiocmget,
.tiocmset = kobil_tiocmset,
.open = kobil_open,
.close = kobil_close,
.write = kobil_write,
.write_room = kobil_write_room,
.read_int_callback = kobil_read_int_callback,
.write_int_callback = kobil_write_int_callback,
};
static struct usb_serial_driver * const serial_drivers[] = {
&kobil_device, NULL
};
struct kobil_private {
unsigned char buf[KOBIL_BUF_LENGTH]; /* buffer for the APDU to send */
int filled; /* index of the last char in buf */
int cur_pos; /* index of the next char to send in buf */
__u16 device_type;
};
static int kobil_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct kobil_private *priv;
priv = kmalloc(sizeof(struct kobil_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->filled = 0;
priv->cur_pos = 0;
priv->device_type = le16_to_cpu(serial->dev->descriptor.idProduct);
switch (priv->device_type) {
case KOBIL_ADAPTER_B_PRODUCT_ID:
dev_dbg(&serial->dev->dev, "KOBIL B1 PRO / KAAN PRO detected\n");
break;
case KOBIL_ADAPTER_K_PRODUCT_ID:
dev_dbg(&serial->dev->dev, "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n");
break;
case KOBIL_USBTWIN_PRODUCT_ID:
dev_dbg(&serial->dev->dev, "KOBIL USBTWIN detected\n");
break;
case KOBIL_KAAN_SIM_PRODUCT_ID:
dev_dbg(&serial->dev->dev, "KOBIL KAAN SIM detected\n");
break;
}
usb_set_serial_port_data(port, priv);
return 0;
}
static int kobil_port_remove(struct usb_serial_port *port)
{
struct kobil_private *priv;
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static void kobil_init_termios(struct tty_struct *tty)
{
/* Default to echo off and other sane device settings */
tty->termios.c_lflag = 0;
tty->termios.c_iflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE);
tty->termios.c_iflag |= IGNBRK | IGNPAR | IXOFF;
/* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */
tty->termios.c_oflag &= ~ONLCR;
}
static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct device *dev = &port->dev;
int result = 0;
struct kobil_private *priv;
unsigned char *transfer_buffer;
int transfer_buffer_length = 8;
priv = usb_get_serial_port_data(port);
/* allocate memory for transfer buffer */
transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
if (!transfer_buffer)
return -ENOMEM;
/* get hardware version */
result = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
SUSBCRequest_GetMisc,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
SUSBCR_MSC_GetHWVersion,
0,
transfer_buffer,
transfer_buffer_length,
KOBIL_TIMEOUT
);
dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result);
dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0],
transfer_buffer[1], transfer_buffer[2]);
/* get firmware version */
result = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
SUSBCRequest_GetMisc,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
SUSBCR_MSC_GetFWVersion,
0,
transfer_buffer,
transfer_buffer_length,
KOBIL_TIMEOUT
);
dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result);
dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0],
transfer_buffer[1], transfer_buffer[2]);
if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
/* Setting Baudrate, Parity and Stopbits */
result = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_SetBaudRateParityAndStopBits,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity |
SUSBCR_SPASB_1StopBit,
0,
NULL,
0,
KOBIL_TIMEOUT
);
dev_dbg(dev, "%s - Send set_baudrate URB returns: %i\n", __func__, result);
/* reset all queues */
result = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_Misc,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
SUSBCR_MSC_ResetAllQueues,
0,
NULL,
0,
KOBIL_TIMEOUT
);
dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result);
}
if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
/* start reading (Adapter B 'cause PNP string) */
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
dev_dbg(dev, "%s - Send read URB returns: %i\n", __func__, result);
}
kfree(transfer_buffer);
return 0;
}
static void kobil_close(struct usb_serial_port *port)
{
/* FIXME: Add rts/dtr methods */
usb_kill_urb(port->interrupt_out_urb);
usb_kill_urb(port->interrupt_in_urb);
}
static void kobil_read_int_callback(struct urb *urb)
{
int result;
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
int status = urb->status;
if (status) {
dev_dbg(&port->dev, "%s - Read int status not zero: %d\n", __func__, status);
return;
}
if (urb->actual_length) {
usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
data);
tty_insert_flip_string(&port->port, data, urb->actual_length);
tty_flip_buffer_push(&port->port);
}
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
}
static void kobil_write_int_callback(struct urb *urb)
{
}
static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
{
int length = 0;
int result = 0;
int todo = 0;
struct kobil_private *priv;
if (count == 0) {
dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
return 0;
}
priv = usb_get_serial_port_data(port);
if (count > (KOBIL_BUF_LENGTH - priv->filled)) {
dev_dbg(&port->dev, "%s - Error: write request bigger than buffer size\n", __func__);
return -ENOMEM;
}
/* Copy data to buffer */
memcpy(priv->buf + priv->filled, buf, count);
usb_serial_debug_data(&port->dev, __func__, count, priv->buf + priv->filled);
priv->filled = priv->filled + count;
/* only send complete block. TWIN, KAAN SIM and adapter K
use the same protocol. */
if (((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) ||
((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4)))) {
/* stop reading (except TWIN and KAAN SIM) */
if ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID)
|| (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID))
usb_kill_urb(port->interrupt_in_urb);
todo = priv->filled - priv->cur_pos;
while (todo > 0) {
/* max 8 byte in one urb (endpoint size) */
length = min(todo, port->interrupt_out_size);
/* copy data to transfer buffer */
memcpy(port->interrupt_out_buffer,
priv->buf + priv->cur_pos, length);
port->interrupt_out_urb->transfer_buffer_length = length;
priv->cur_pos = priv->cur_pos + length;
result = usb_submit_urb(port->interrupt_out_urb,
GFP_ATOMIC);
dev_dbg(&port->dev, "%s - Send write URB returns: %i\n", __func__, result);
todo = priv->filled - priv->cur_pos;
if (todo > 0)
msleep(24);
}
priv->filled = 0;
priv->cur_pos = 0;
/* start reading (except TWIN and KAAN SIM) */
if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
result = usb_submit_urb(port->interrupt_in_urb,
GFP_ATOMIC);
dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
}
}
return count;
}
static int kobil_write_room(struct tty_struct *tty)
{
/* FIXME */
return 8;
}
static int kobil_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct kobil_private *priv;
int result;
unsigned char *transfer_buffer;
int transfer_buffer_length = 8;
priv = usb_get_serial_port_data(port);
if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
|| priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
/* This device doesn't support ioctl calls */
return -EINVAL;
}
/* allocate memory for transfer buffer */
transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
if (!transfer_buffer)
return -ENOMEM;
result = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
SUSBCRequest_GetStatusLineState,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
0,
0,
transfer_buffer,
transfer_buffer_length,
KOBIL_TIMEOUT);
dev_dbg(&port->dev, "%s - Send get_status_line_state URB returns: %i. Statusline: %02x\n",
__func__, result, transfer_buffer[0]);
result = 0;
if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0)
result = TIOCM_DSR;
kfree(transfer_buffer);
return result;
}
static int kobil_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct device *dev = &port->dev;
struct kobil_private *priv;
int result;
int dtr = 0;
int rts = 0;
/* FIXME: locking ? */
priv = usb_get_serial_port_data(port);
if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
|| priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
/* This device doesn't support ioctl calls */
return -EINVAL;
}
if (set & TIOCM_RTS)
rts = 1;
if (set & TIOCM_DTR)
dtr = 1;
if (clear & TIOCM_RTS)
rts = 0;
if (clear & TIOCM_DTR)
dtr = 0;
if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) {
if (dtr != 0)
dev_dbg(dev, "%s - Setting DTR\n", __func__);
else
dev_dbg(dev, "%s - Clearing DTR\n", __func__);
result = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_SetStatusLinesOrQueues,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR),
0,
NULL,
0,
KOBIL_TIMEOUT);
} else {
if (rts != 0)
dev_dbg(dev, "%s - Setting RTS\n", __func__);
else
dev_dbg(dev, "%s - Clearing RTS\n", __func__);
result = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_SetStatusLinesOrQueues,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS),
0,
NULL,
0,
KOBIL_TIMEOUT);
}
dev_dbg(dev, "%s - Send set_status_line URB returns: %i\n", __func__, result);
return (result < 0) ? result : 0;
}
static void kobil_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old)
{
struct kobil_private *priv;
int result;
unsigned short urb_val = 0;
int c_cflag = tty->termios.c_cflag;
speed_t speed;
priv = usb_get_serial_port_data(port);
if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
/* This device doesn't support ioctl calls */
tty_termios_copy_hw(&tty->termios, old);
return;
}
speed = tty_get_baud_rate(tty);
switch (speed) {
case 1200:
urb_val = SUSBCR_SBR_1200;
break;
default:
speed = 9600;
case 9600:
urb_val = SUSBCR_SBR_9600;
break;
}
urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits :
SUSBCR_SPASB_1StopBit;
if (c_cflag & PARENB) {
if (c_cflag & PARODD)
urb_val |= SUSBCR_SPASB_OddParity;
else
urb_val |= SUSBCR_SPASB_EvenParity;
} else
urb_val |= SUSBCR_SPASB_NoParity;
tty->termios.c_cflag &= ~CMSPAR;
tty_encode_baud_rate(tty, speed, speed);
result = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_SetBaudRateParityAndStopBits,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
urb_val,
0,
NULL,
0,
KOBIL_TIMEOUT
);
}
static int kobil_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
struct kobil_private *priv = usb_get_serial_port_data(port);
int result;
if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID)
/* This device doesn't support ioctl calls */
return -ENOIOCTLCMD;
switch (cmd) {
case TCFLSH:
result = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
SUSBCRequest_Misc,
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
SUSBCR_MSC_ResetAllQueues,
0,
NULL,
0,
KOBIL_TIMEOUT
);
dev_dbg(&port->dev,
"%s - Send reset_all_queues (FLUSH) URB returns: %i\n",
__func__, result);
return (result < 0) ? -EIO: 0;
default:
return -ENOIOCTLCMD;
}
}
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,77 @@
#define SUSBCRequest_SetBaudRateParityAndStopBits 1
#define SUSBCR_SBR_MASK 0xFF00
#define SUSBCR_SBR_1200 0x0100
#define SUSBCR_SBR_9600 0x0200
#define SUSBCR_SBR_19200 0x0400
#define SUSBCR_SBR_28800 0x0800
#define SUSBCR_SBR_38400 0x1000
#define SUSBCR_SBR_57600 0x2000
#define SUSBCR_SBR_115200 0x4000
#define SUSBCR_SPASB_MASK 0x0070
#define SUSBCR_SPASB_NoParity 0x0010
#define SUSBCR_SPASB_OddParity 0x0020
#define SUSBCR_SPASB_EvenParity 0x0040
#define SUSBCR_SPASB_STPMASK 0x0003
#define SUSBCR_SPASB_1StopBit 0x0001
#define SUSBCR_SPASB_2StopBits 0x0002
#define SUSBCRequest_SetStatusLinesOrQueues 2
#define SUSBCR_SSL_SETRTS 0x0001
#define SUSBCR_SSL_CLRRTS 0x0002
#define SUSBCR_SSL_SETDTR 0x0004
#define SUSBCR_SSL_CLRDTR 0x0010
/* Kill the pending/current writes to the comm port. */
#define SUSBCR_SSL_PURGE_TXABORT 0x0100
/* Kill the pending/current reads to the comm port. */
#define SUSBCR_SSL_PURGE_RXABORT 0x0200
/* Kill the transmit queue if there. */
#define SUSBCR_SSL_PURGE_TXCLEAR 0x0400
/* Kill the typeahead buffer if there. */
#define SUSBCR_SSL_PURGE_RXCLEAR 0x0800
#define SUSBCRequest_GetStatusLineState 4
/* Any Character received */
#define SUSBCR_GSL_RXCHAR 0x0001
/* Transmitt Queue Empty */
#define SUSBCR_GSL_TXEMPTY 0x0004
/* CTS changed state */
#define SUSBCR_GSL_CTS 0x0008
/* DSR changed state */
#define SUSBCR_GSL_DSR 0x0010
/* RLSD changed state */
#define SUSBCR_GSL_RLSD 0x0020
/* BREAK received */
#define SUSBCR_GSL_BREAK 0x0040
/* Line status error occurred */
#define SUSBCR_GSL_ERR 0x0080
/* Ring signal detected */
#define SUSBCR_GSL_RING 0x0100
#define SUSBCRequest_Misc 8
/* use a predefined reset sequence */
#define SUSBCR_MSC_ResetReader 0x0001
/* use a predefined sequence to reset the internal queues */
#define SUSBCR_MSC_ResetAllQueues 0x0002
#define SUSBCRequest_GetMisc 0x10
/*
* get the firmware version from device, coded like this 0xHHLLBBPP with
* HH = Firmware Version High Byte
* LL = Firmware Version Low Byte
* BB = Build Number
* PP = Further Attributes
*/
#define SUSBCR_MSC_GetFWVersion 0x0001
/*
* get the hardware version from device coded like this 0xHHLLPPRR with
* HH = Software Version High Byte
* LL = Software Version Low Byte
* PP = Further Attributes
* RR = Reserved for the hardware ID
*/
#define SUSBCR_MSC_GetHWVersion 0x0002

View file

@ -0,0 +1,771 @@
/*
* MCT (Magic Control Technology Corp.) USB RS232 Converter Driver
*
* Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is largely derived from the Belkin USB Serial Adapter Driver
* (see belkin_sa.[ch]). All of the information about the device was acquired
* by using SniffUSB on Windows98. For technical details see mct_u232.h.
*
* William G. Greathouse and Greg Kroah-Hartman provided great help on how to
* do the reverse engineering and how to write a USB serial device driver.
*
* TO BE DONE, TO BE CHECKED:
* DTR/RTS signal handling may be incomplete or incorrect. I have mainly
* implemented what I have seen with SniffUSB or found in belkin_sa.c.
* For further TODOs check also belkin_sa.c.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial.h>
#include "mct_u232.h"
#define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
#define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
/*
* Function prototypes
*/
static int mct_u232_port_probe(struct usb_serial_port *port);
static int mct_u232_port_remove(struct usb_serial_port *remove);
static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port);
static void mct_u232_close(struct usb_serial_port *port);
static void mct_u232_dtr_rts(struct usb_serial_port *port, int on);
static void mct_u232_read_int_callback(struct urb *urb);
static void mct_u232_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old);
static void mct_u232_break_ctl(struct tty_struct *tty, int break_state);
static int mct_u232_tiocmget(struct tty_struct *tty);
static int mct_u232_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
static void mct_u232_throttle(struct tty_struct *tty);
static void mct_u232_unthrottle(struct tty_struct *tty);
/*
* All of the device info needed for the MCT USB-RS232 converter.
*/
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(MCT_U232_VID, MCT_U232_PID) },
{ USB_DEVICE(MCT_U232_VID, MCT_U232_SITECOM_PID) },
{ USB_DEVICE(MCT_U232_VID, MCT_U232_DU_H3SP_PID) },
{ USB_DEVICE(MCT_U232_BELKIN_F5U109_VID, MCT_U232_BELKIN_F5U109_PID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver mct_u232_device = {
.driver = {
.owner = THIS_MODULE,
.name = "mct_u232",
},
.description = "MCT U232",
.id_table = id_table,
.num_ports = 1,
.open = mct_u232_open,
.close = mct_u232_close,
.dtr_rts = mct_u232_dtr_rts,
.throttle = mct_u232_throttle,
.unthrottle = mct_u232_unthrottle,
.read_int_callback = mct_u232_read_int_callback,
.set_termios = mct_u232_set_termios,
.break_ctl = mct_u232_break_ctl,
.tiocmget = mct_u232_tiocmget,
.tiocmset = mct_u232_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.port_probe = mct_u232_port_probe,
.port_remove = mct_u232_port_remove,
.get_icount = usb_serial_generic_get_icount,
};
static struct usb_serial_driver * const serial_drivers[] = {
&mct_u232_device, NULL
};
struct mct_u232_private {
struct urb *read_urb;
spinlock_t lock;
unsigned int control_state; /* Modem Line Setting (TIOCM) */
unsigned char last_lcr; /* Line Control Register */
unsigned char last_lsr; /* Line Status Register */
unsigned char last_msr; /* Modem Status Register */
unsigned int rx_flags; /* Throttling flags */
};
#define THROTTLED 0x01
/*
* Handle vendor specific USB requests
*/
#define WDR_TIMEOUT 5000 /* default urb timeout */
/*
* Later day 2.6.0-test kernels have new baud rates like B230400 which
* we do not know how to support. We ignore them for the moment.
*/
static int mct_u232_calculate_baud_rate(struct usb_serial *serial,
speed_t value, speed_t *result)
{
*result = value;
if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID
|| le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) {
switch (value) {
case 300:
return 0x01;
case 600:
return 0x02; /* this one not tested */
case 1200:
return 0x03;
case 2400:
return 0x04;
case 4800:
return 0x06;
case 9600:
return 0x08;
case 19200:
return 0x09;
case 38400:
return 0x0a;
case 57600:
return 0x0b;
case 115200:
return 0x0c;
default:
*result = 9600;
return 0x08;
}
} else {
/* FIXME: Can we use any divider - should we do
divider = 115200/value;
real baud = 115200/divider */
switch (value) {
case 300: break;
case 600: break;
case 1200: break;
case 2400: break;
case 4800: break;
case 9600: break;
case 19200: break;
case 38400: break;
case 57600: break;
case 115200: break;
default:
value = 9600;
*result = 9600;
}
return 115200/value;
}
}
static int mct_u232_set_baud_rate(struct tty_struct *tty,
struct usb_serial *serial, struct usb_serial_port *port, speed_t value)
{
unsigned int divisor;
int rc;
unsigned char *buf;
unsigned char cts_enable_byte = 0;
speed_t speed;
buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
divisor = mct_u232_calculate_baud_rate(serial, value, &speed);
put_unaligned_le32(cpu_to_le32(divisor), buf);
rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
MCT_U232_SET_BAUD_RATE_REQUEST,
MCT_U232_SET_REQUEST_TYPE,
0, 0, buf, MCT_U232_SET_BAUD_RATE_SIZE,
WDR_TIMEOUT);
if (rc < 0) /*FIXME: What value speed results */
dev_err(&port->dev, "Set BAUD RATE %d failed (error = %d)\n",
value, rc);
else
tty_encode_baud_rate(tty, speed, speed);
dev_dbg(&port->dev, "set_baud_rate: value: 0x%x, divisor: 0x%x\n", value, divisor);
/* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which
always sends two extra USB 'device request' messages after the
'baud rate change' message. The actual functionality of the
request codes in these messages is not fully understood but these
particular codes are never seen in any operation besides a baud
rate change. Both of these messages send a single byte of data.
In the first message, the value of this byte is always zero.
The second message has been determined experimentally to control
whether data will be transmitted to a device which is not asserting
the 'CTS' signal. If the second message's data byte is zero, data
will be transmitted even if 'CTS' is not asserted (i.e. no hardware
flow control). if the second message's data byte is nonzero (a
value of 1 is used by this driver), data will not be transmitted to
a device which is not asserting 'CTS'.
*/
buf[0] = 0;
rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
MCT_U232_SET_UNKNOWN1_REQUEST,
MCT_U232_SET_REQUEST_TYPE,
0, 0, buf, MCT_U232_SET_UNKNOWN1_SIZE,
WDR_TIMEOUT);
if (rc < 0)
dev_err(&port->dev, "Sending USB device request code %d "
"failed (error = %d)\n", MCT_U232_SET_UNKNOWN1_REQUEST,
rc);
if (port && C_CRTSCTS(tty))
cts_enable_byte = 1;
dev_dbg(&port->dev, "set_baud_rate: send second control message, data = %02X\n",
cts_enable_byte);
buf[0] = cts_enable_byte;
rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
MCT_U232_SET_CTS_REQUEST,
MCT_U232_SET_REQUEST_TYPE,
0, 0, buf, MCT_U232_SET_CTS_SIZE,
WDR_TIMEOUT);
if (rc < 0)
dev_err(&port->dev, "Sending USB device request code %d "
"failed (error = %d)\n", MCT_U232_SET_CTS_REQUEST, rc);
kfree(buf);
return rc;
} /* mct_u232_set_baud_rate */
static int mct_u232_set_line_ctrl(struct usb_serial_port *port,
unsigned char lcr)
{
int rc;
unsigned char *buf;
buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
buf[0] = lcr;
rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
MCT_U232_SET_LINE_CTRL_REQUEST,
MCT_U232_SET_REQUEST_TYPE,
0, 0, buf, MCT_U232_SET_LINE_CTRL_SIZE,
WDR_TIMEOUT);
if (rc < 0)
dev_err(&port->dev, "Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc);
dev_dbg(&port->dev, "set_line_ctrl: 0x%x\n", lcr);
kfree(buf);
return rc;
} /* mct_u232_set_line_ctrl */
static int mct_u232_set_modem_ctrl(struct usb_serial_port *port,
unsigned int control_state)
{
int rc;
unsigned char mcr;
unsigned char *buf;
buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
mcr = MCT_U232_MCR_NONE;
if (control_state & TIOCM_DTR)
mcr |= MCT_U232_MCR_DTR;
if (control_state & TIOCM_RTS)
mcr |= MCT_U232_MCR_RTS;
buf[0] = mcr;
rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
MCT_U232_SET_MODEM_CTRL_REQUEST,
MCT_U232_SET_REQUEST_TYPE,
0, 0, buf, MCT_U232_SET_MODEM_CTRL_SIZE,
WDR_TIMEOUT);
kfree(buf);
dev_dbg(&port->dev, "set_modem_ctrl: state=0x%x ==> mcr=0x%x\n", control_state, mcr);
if (rc < 0) {
dev_err(&port->dev, "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);
return rc;
}
return 0;
} /* mct_u232_set_modem_ctrl */
static int mct_u232_get_modem_stat(struct usb_serial_port *port,
unsigned char *msr)
{
int rc;
unsigned char *buf;
buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
if (buf == NULL) {
*msr = 0;
return -ENOMEM;
}
rc = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0),
MCT_U232_GET_MODEM_STAT_REQUEST,
MCT_U232_GET_REQUEST_TYPE,
0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE,
WDR_TIMEOUT);
if (rc < 0) {
dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc);
*msr = 0;
} else {
*msr = buf[0];
}
dev_dbg(&port->dev, "get_modem_stat: 0x%x\n", *msr);
kfree(buf);
return rc;
} /* mct_u232_get_modem_stat */
static void mct_u232_msr_to_icount(struct async_icount *icount,
unsigned char msr)
{
/* Translate Control Line states */
if (msr & MCT_U232_MSR_DDSR)
icount->dsr++;
if (msr & MCT_U232_MSR_DCTS)
icount->cts++;
if (msr & MCT_U232_MSR_DRI)
icount->rng++;
if (msr & MCT_U232_MSR_DCD)
icount->dcd++;
} /* mct_u232_msr_to_icount */
static void mct_u232_msr_to_state(struct usb_serial_port *port,
unsigned int *control_state, unsigned char msr)
{
/* Translate Control Line states */
if (msr & MCT_U232_MSR_DSR)
*control_state |= TIOCM_DSR;
else
*control_state &= ~TIOCM_DSR;
if (msr & MCT_U232_MSR_CTS)
*control_state |= TIOCM_CTS;
else
*control_state &= ~TIOCM_CTS;
if (msr & MCT_U232_MSR_RI)
*control_state |= TIOCM_RI;
else
*control_state &= ~TIOCM_RI;
if (msr & MCT_U232_MSR_CD)
*control_state |= TIOCM_CD;
else
*control_state &= ~TIOCM_CD;
dev_dbg(&port->dev, "msr_to_state: msr=0x%x ==> state=0x%x\n", msr, *control_state);
} /* mct_u232_msr_to_state */
/*
* Driver's tty interface functions
*/
static int mct_u232_port_probe(struct usb_serial_port *port)
{
struct mct_u232_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* Use second interrupt-in endpoint for reading. */
priv->read_urb = port->serial->port[1]->interrupt_in_urb;
priv->read_urb->context = port;
spin_lock_init(&priv->lock);
usb_set_serial_port_data(port, priv);
return 0;
}
static int mct_u232_port_remove(struct usb_serial_port *port)
{
struct mct_u232_private *priv;
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
int retval = 0;
unsigned int control_state;
unsigned long flags;
unsigned char last_lcr;
unsigned char last_msr;
/* Compensate for a hardware bug: although the Sitecom U232-P25
* device reports a maximum output packet size of 32 bytes,
* it seems to be able to accept only 16 bytes (and that's what
* SniffUSB says too...)
*/
if (le16_to_cpu(serial->dev->descriptor.idProduct)
== MCT_U232_SITECOM_PID)
port->bulk_out_size = 16;
/* Do a defined restart: the normal serial device seems to
* always turn on DTR and RTS here, so do the same. I'm not
* sure if this is really necessary. But it should not harm
* either.
*/
spin_lock_irqsave(&priv->lock, flags);
if (tty && (tty->termios.c_cflag & CBAUD))
priv->control_state = TIOCM_DTR | TIOCM_RTS;
else
priv->control_state = 0;
priv->last_lcr = (MCT_U232_DATA_BITS_8 |
MCT_U232_PARITY_NONE |
MCT_U232_STOP_BITS_1);
control_state = priv->control_state;
last_lcr = priv->last_lcr;
spin_unlock_irqrestore(&priv->lock, flags);
mct_u232_set_modem_ctrl(port, control_state);
mct_u232_set_line_ctrl(port, last_lcr);
/* Read modem status and update control state */
mct_u232_get_modem_stat(port, &last_msr);
spin_lock_irqsave(&priv->lock, flags);
priv->last_msr = last_msr;
mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
spin_unlock_irqrestore(&priv->lock, flags);
retval = usb_submit_urb(priv->read_urb, GFP_KERNEL);
if (retval) {
dev_err(&port->dev,
"usb_submit_urb(read) failed pipe 0x%x err %d\n",
port->read_urb->pipe, retval);
goto error;
}
retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (retval) {
usb_kill_urb(priv->read_urb);
dev_err(&port->dev,
"usb_submit_urb(read int) failed pipe 0x%x err %d",
port->interrupt_in_urb->pipe, retval);
goto error;
}
return 0;
error:
return retval;
} /* mct_u232_open */
static void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
{
unsigned int control_state;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
spin_lock_irq(&priv->lock);
if (on)
priv->control_state |= TIOCM_DTR | TIOCM_RTS;
else
priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
control_state = priv->control_state;
spin_unlock_irq(&priv->lock);
mct_u232_set_modem_ctrl(port, control_state);
}
static void mct_u232_close(struct usb_serial_port *port)
{
struct mct_u232_private *priv = usb_get_serial_port_data(port);
usb_kill_urb(priv->read_urb);
usb_kill_urb(port->interrupt_in_urb);
usb_serial_generic_close(port);
} /* mct_u232_close */
static void mct_u232_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
int retval;
int status = urb->status;
unsigned long flags;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
__func__, status);
goto exit;
}
usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
/*
* Work-a-round: handle the 'usual' bulk-in pipe here
*/
if (urb->transfer_buffer_length > 2) {
if (urb->actual_length) {
tty_insert_flip_string(&port->port, data,
urb->actual_length);
tty_flip_buffer_push(&port->port);
}
goto exit;
}
/*
* The interrupt-in pipe signals exceptional conditions (modem line
* signal changes and errors). data[0] holds MSR, data[1] holds LSR.
*/
spin_lock_irqsave(&priv->lock, flags);
priv->last_msr = data[MCT_U232_MSR_INDEX];
/* Record Control Line states */
mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
mct_u232_msr_to_icount(&port->icount, priv->last_msr);
#if 0
/* Not yet handled. See belkin_sa.c for further information */
/* Now to report any errors */
priv->last_lsr = data[MCT_U232_LSR_INDEX];
/*
* fill in the flip buffer here, but I do not know the relation
* to the current/next receive buffer or characters. I need
* to look in to this before committing any code.
*/
if (priv->last_lsr & MCT_U232_LSR_ERR) {
tty = tty_port_tty_get(&port->port);
/* Overrun Error */
if (priv->last_lsr & MCT_U232_LSR_OE) {
}
/* Parity Error */
if (priv->last_lsr & MCT_U232_LSR_PE) {
}
/* Framing Error */
if (priv->last_lsr & MCT_U232_LSR_FE) {
}
/* Break Indicator */
if (priv->last_lsr & MCT_U232_LSR_BI) {
}
tty_kref_put(tty);
}
#endif
wake_up_interruptible(&port->port.delta_msr_wait);
spin_unlock_irqrestore(&priv->lock, flags);
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
dev_err(&port->dev,
"%s - usb_submit_urb failed with result %d\n",
__func__, retval);
} /* mct_u232_read_int_callback */
static void mct_u232_set_termios(struct tty_struct *tty,
struct usb_serial_port *port,
struct ktermios *old_termios)
{
struct usb_serial *serial = port->serial;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
struct ktermios *termios = &tty->termios;
unsigned int cflag = termios->c_cflag;
unsigned int old_cflag = old_termios->c_cflag;
unsigned long flags;
unsigned int control_state;
unsigned char last_lcr;
/* get a local copy of the current port settings */
spin_lock_irqsave(&priv->lock, flags);
control_state = priv->control_state;
spin_unlock_irqrestore(&priv->lock, flags);
last_lcr = 0;
/*
* Update baud rate.
* Do not attempt to cache old rates and skip settings,
* disconnects screw such tricks up completely.
* Premature optimization is the root of all evil.
*/
/* reassert DTR and RTS on transition from B0 */
if ((old_cflag & CBAUD) == B0) {
dev_dbg(&port->dev, "%s: baud was B0\n", __func__);
control_state |= TIOCM_DTR | TIOCM_RTS;
mct_u232_set_modem_ctrl(port, control_state);
}
mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty));
if ((cflag & CBAUD) == B0) {
dev_dbg(&port->dev, "%s: baud is B0\n", __func__);
/* Drop RTS and DTR */
control_state &= ~(TIOCM_DTR | TIOCM_RTS);
mct_u232_set_modem_ctrl(port, control_state);
}
/*
* Update line control register (LCR)
*/
/* set the parity */
if (cflag & PARENB)
last_lcr |= (cflag & PARODD) ?
MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
else
last_lcr |= MCT_U232_PARITY_NONE;
/* set the number of data bits */
switch (cflag & CSIZE) {
case CS5:
last_lcr |= MCT_U232_DATA_BITS_5; break;
case CS6:
last_lcr |= MCT_U232_DATA_BITS_6; break;
case CS7:
last_lcr |= MCT_U232_DATA_BITS_7; break;
case CS8:
last_lcr |= MCT_U232_DATA_BITS_8; break;
default:
dev_err(&port->dev,
"CSIZE was not CS5-CS8, using default of 8\n");
last_lcr |= MCT_U232_DATA_BITS_8;
break;
}
termios->c_cflag &= ~CMSPAR;
/* set the number of stop bits */
last_lcr |= (cflag & CSTOPB) ?
MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
mct_u232_set_line_ctrl(port, last_lcr);
/* save off the modified port settings */
spin_lock_irqsave(&priv->lock, flags);
priv->control_state = control_state;
priv->last_lcr = last_lcr;
spin_unlock_irqrestore(&priv->lock, flags);
} /* mct_u232_set_termios */
static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
unsigned char lcr;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
lcr = priv->last_lcr;
if (break_state)
lcr |= MCT_U232_SET_BREAK;
spin_unlock_irqrestore(&priv->lock, flags);
mct_u232_set_line_ctrl(port, lcr);
} /* mct_u232_break_ctl */
static int mct_u232_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
unsigned int control_state;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
control_state = priv->control_state;
spin_unlock_irqrestore(&priv->lock, flags);
return control_state;
}
static int mct_u232_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
unsigned int control_state;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
control_state = priv->control_state;
if (set & TIOCM_RTS)
control_state |= TIOCM_RTS;
if (set & TIOCM_DTR)
control_state |= TIOCM_DTR;
if (clear & TIOCM_RTS)
control_state &= ~TIOCM_RTS;
if (clear & TIOCM_DTR)
control_state &= ~TIOCM_DTR;
priv->control_state = control_state;
spin_unlock_irqrestore(&priv->lock, flags);
return mct_u232_set_modem_ctrl(port, control_state);
}
static void mct_u232_throttle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
unsigned int control_state;
spin_lock_irq(&priv->lock);
priv->rx_flags |= THROTTLED;
if (C_CRTSCTS(tty)) {
priv->control_state &= ~TIOCM_RTS;
control_state = priv->control_state;
spin_unlock_irq(&priv->lock);
mct_u232_set_modem_ctrl(port, control_state);
} else {
spin_unlock_irq(&priv->lock);
}
}
static void mct_u232_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
unsigned int control_state;
spin_lock_irq(&priv->lock);
if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) {
priv->rx_flags &= ~THROTTLED;
priv->control_state |= TIOCM_RTS;
control_state = priv->control_state;
spin_unlock_irq(&priv->lock);
mct_u232_set_modem_ctrl(port, control_state);
} else {
spin_unlock_irq(&priv->lock);
}
}
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,467 @@
/*
* Definitions for MCT (Magic Control Technology) USB-RS232 Converter Driver
*
* Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver is for the device MCT USB-RS232 Converter (25 pin, Model No.
* U232-P25) from Magic Control Technology Corp. (there is also a 9 pin
* Model No. U232-P9). See http://www.mct.com.tw/products/product_us232.html
* for further information. The properties of this device are listed at the end
* of this file. This device was used in the Dlink DSB-S25.
*
* All of the information about the device was acquired by using SniffUSB
* on Windows98. The technical details of the reverse engineering are
* summarized at the end of this file.
*/
#ifndef __LINUX_USB_SERIAL_MCT_U232_H
#define __LINUX_USB_SERIAL_MCT_U232_H
#define MCT_U232_VID 0x0711 /* Vendor Id */
#define MCT_U232_PID 0x0210 /* Original MCT Product Id */
/* U232-P25, Sitecom */
#define MCT_U232_SITECOM_PID 0x0230 /* Sitecom Product Id */
/* DU-H3SP USB BAY hub */
#define MCT_U232_DU_H3SP_PID 0x0200 /* D-Link DU-H3SP USB BAY */
/* Belkin badge the MCT U232-P9 as the F5U109 */
#define MCT_U232_BELKIN_F5U109_VID 0x050d /* Vendor Id */
#define MCT_U232_BELKIN_F5U109_PID 0x0109 /* Product Id */
/*
* Vendor Request Interface
*/
#define MCT_U232_SET_REQUEST_TYPE 0x40
#define MCT_U232_GET_REQUEST_TYPE 0xc0
/* Get Modem Status Register (MSR) */
#define MCT_U232_GET_MODEM_STAT_REQUEST 2
#define MCT_U232_GET_MODEM_STAT_SIZE 1
/* Get Line Control Register (LCR) */
/* ... not used by this driver */
#define MCT_U232_GET_LINE_CTRL_REQUEST 6
#define MCT_U232_GET_LINE_CTRL_SIZE 1
/* Set Baud Rate Divisor */
#define MCT_U232_SET_BAUD_RATE_REQUEST 5
#define MCT_U232_SET_BAUD_RATE_SIZE 4
/* Set Line Control Register (LCR) */
#define MCT_U232_SET_LINE_CTRL_REQUEST 7
#define MCT_U232_SET_LINE_CTRL_SIZE 1
/* Set Modem Control Register (MCR) */
#define MCT_U232_SET_MODEM_CTRL_REQUEST 10
#define MCT_U232_SET_MODEM_CTRL_SIZE 1
/*
* This USB device request code is not well understood. It is transmitted by
* the MCT-supplied Windows driver whenever the baud rate changes.
*/
#define MCT_U232_SET_UNKNOWN1_REQUEST 11 /* Unknown functionality */
#define MCT_U232_SET_UNKNOWN1_SIZE 1
/*
* This USB device request code appears to control whether CTS is required
* during transmission.
*
* Sending a zero byte allows data transmission to a device which is not
* asserting CTS. Sending a '1' byte will cause transmission to be deferred
* until the device asserts CTS.
*/
#define MCT_U232_SET_CTS_REQUEST 12
#define MCT_U232_SET_CTS_SIZE 1
#define MCT_U232_MAX_SIZE 4 /* of MCT_XXX_SIZE */
/*
* Baud rate (divisor)
* Actually, there are two of them, MCT website calls them "Philips solution"
* and "Intel solution". They are the regular MCT and "Sitecom" for us.
* This is pointless to document in the header, see the code for the bits.
*/
static int mct_u232_calculate_baud_rate(struct usb_serial *serial,
speed_t value, speed_t *result);
/*
* Line Control Register (LCR)
*/
#define MCT_U232_SET_BREAK 0x40
#define MCT_U232_PARITY_SPACE 0x38
#define MCT_U232_PARITY_MARK 0x28
#define MCT_U232_PARITY_EVEN 0x18
#define MCT_U232_PARITY_ODD 0x08
#define MCT_U232_PARITY_NONE 0x00
#define MCT_U232_DATA_BITS_5 0x00
#define MCT_U232_DATA_BITS_6 0x01
#define MCT_U232_DATA_BITS_7 0x02
#define MCT_U232_DATA_BITS_8 0x03
#define MCT_U232_STOP_BITS_2 0x04
#define MCT_U232_STOP_BITS_1 0x00
/*
* Modem Control Register (MCR)
*/
#define MCT_U232_MCR_NONE 0x8 /* Deactivate DTR and RTS */
#define MCT_U232_MCR_RTS 0xa /* Activate RTS */
#define MCT_U232_MCR_DTR 0x9 /* Activate DTR */
/*
* Modem Status Register (MSR)
*/
#define MCT_U232_MSR_INDEX 0x0 /* data[index] */
#define MCT_U232_MSR_CD 0x80 /* Current CD */
#define MCT_U232_MSR_RI 0x40 /* Current RI */
#define MCT_U232_MSR_DSR 0x20 /* Current DSR */
#define MCT_U232_MSR_CTS 0x10 /* Current CTS */
#define MCT_U232_MSR_DCD 0x08 /* Delta CD */
#define MCT_U232_MSR_DRI 0x04 /* Delta RI */
#define MCT_U232_MSR_DDSR 0x02 /* Delta DSR */
#define MCT_U232_MSR_DCTS 0x01 /* Delta CTS */
/*
* Line Status Register (LSR)
*/
#define MCT_U232_LSR_INDEX 1 /* data[index] */
#define MCT_U232_LSR_ERR 0x80 /* OE | PE | FE | BI */
#define MCT_U232_LSR_TEMT 0x40 /* transmit register empty */
#define MCT_U232_LSR_THRE 0x20 /* transmit holding register empty */
#define MCT_U232_LSR_BI 0x10 /* break indicator */
#define MCT_U232_LSR_FE 0x08 /* framing error */
#define MCT_U232_LSR_OE 0x02 /* overrun error */
#define MCT_U232_LSR_PE 0x04 /* parity error */
#define MCT_U232_LSR_OE 0x02 /* overrun error */
#define MCT_U232_LSR_DR 0x01 /* receive data ready */
/* -----------------------------------------------------------------------------
* Technical Specification reverse engineered with SniffUSB on Windows98
* =====================================================================
*
* The technical details of the device have been acquired be using "SniffUSB"
* and the vendor-supplied device driver (version 2.3A) under Windows98. To
* identify the USB vendor-specific requests and to assign them to terminal
* settings (flow control, baud rate, etc.) the program "SerialSettings" from
* William G. Greathouse has been proven to be very useful. I also used the
* Win98 "HyperTerminal" and "usb-robot" on Linux for testing. The results and
* observations are summarized below:
*
* The USB requests seem to be directly mapped to the registers of a 8250,
* 16450 or 16550 UART. The FreeBSD handbook (appendix F.4 "Input/Output
* devices") contains a comprehensive description of UARTs and its registers.
* The bit descriptions are actually taken from there.
*
*
* Baud rate (divisor)
* -------------------
*
* BmRequestType: 0x40 (0100 0000B)
* bRequest: 0x05
* wValue: 0x0000
* wIndex: 0x0000
* wLength: 0x0004
* Data: divisor = 115200 / baud_rate
*
* SniffUSB observations (Nov 2003): Contrary to the 'wLength' value of 4
* shown above, observations with a Belkin F5U109 adapter, using the
* MCT-supplied Windows98 driver (U2SPORT.VXD, "File version: 1.21P.0104 for
* Win98/Me"), show this request has a length of 1 byte, presumably because
* of the fact that the Belkin adapter and the 'Sitecom U232-P25' adapter
* use a baud-rate code instead of a conventional RS-232 baud rate divisor.
* The current source code for this driver does not reflect this fact, but
* the driver works fine with this adapter/driver combination nonetheless.
*
*
* Line Control Register (LCR)
* ---------------------------
*
* BmRequestType: 0x40 (0100 0000B) 0xc0 (1100 0000B)
* bRequest: 0x07 0x06
* wValue: 0x0000
* wIndex: 0x0000
* wLength: 0x0001
* Data: LCR (see below)
*
* Bit 7: Divisor Latch Access Bit (DLAB). When set, access to the data
* transmit/receive register (THR/RBR) and the Interrupt Enable Register
* (IER) is disabled. Any access to these ports is now redirected to the
* Divisor Latch Registers. Setting this bit, loading the Divisor
* Registers, and clearing DLAB should be done with interrupts disabled.
* Bit 6: Set Break. When set to "1", the transmitter begins to transmit
* continuous Spacing until this bit is set to "0". This overrides any
* bits of characters that are being transmitted.
* Bit 5: Stick Parity. When parity is enabled, setting this bit causes parity
* to always be "1" or "0", based on the value of Bit 4.
* Bit 4: Even Parity Select (EPS). When parity is enabled and Bit 5 is "0",
* setting this bit causes even parity to be transmitted and expected.
* Otherwise, odd parity is used.
* Bit 3: Parity Enable (PEN). When set to "1", a parity bit is inserted
* between the last bit of the data and the Stop Bit. The UART will also
* expect parity to be present in the received data.
* Bit 2: Number of Stop Bits (STB). If set to "1" and using 5-bit data words,
* 1.5 Stop Bits are transmitted and expected in each data word. For
* 6, 7 and 8-bit data words, 2 Stop Bits are transmitted and expected.
* When this bit is set to "0", one Stop Bit is used on each data word.
* Bit 1: Word Length Select Bit #1 (WLSB1)
* Bit 0: Word Length Select Bit #0 (WLSB0)
* Together these bits specify the number of bits in each data word.
* 1 0 Word Length
* 0 0 5 Data Bits
* 0 1 6 Data Bits
* 1 0 7 Data Bits
* 1 1 8 Data Bits
*
* SniffUSB observations: Bit 7 seems not to be used. There seem to be two bugs
* in the Win98 driver: the break does not work (bit 6 is not asserted) and the
* stick parity bit is not cleared when set once. The LCR can also be read
* back with USB request 6 but this has never been observed with SniffUSB.
*
*
* Modem Control Register (MCR)
* ----------------------------
*
* BmRequestType: 0x40 (0100 0000B)
* bRequest: 0x0a
* wValue: 0x0000
* wIndex: 0x0000
* wLength: 0x0001
* Data: MCR (Bit 4..7, see below)
*
* Bit 7: Reserved, always 0.
* Bit 6: Reserved, always 0.
* Bit 5: Reserved, always 0.
* Bit 4: Loop-Back Enable. When set to "1", the UART transmitter and receiver
* are internally connected together to allow diagnostic operations. In
* addition, the UART modem control outputs are connected to the UART
* modem control inputs. CTS is connected to RTS, DTR is connected to
* DSR, OUT1 is connected to RI, and OUT 2 is connected to DCD.
* Bit 3: OUT 2. An auxiliary output that the host processor may set high or
* low. In the IBM PC serial adapter (and most clones), OUT 2 is used
* to tri-state (disable) the interrupt signal from the
* 8250/16450/16550 UART.
* Bit 2: OUT 1. An auxiliary output that the host processor may set high or
* low. This output is not used on the IBM PC serial adapter.
* Bit 1: Request to Send (RTS). When set to "1", the output of the UART -RTS
* line is Low (Active).
* Bit 0: Data Terminal Ready (DTR). When set to "1", the output of the UART
* -DTR line is Low (Active).
*
* SniffUSB observations: Bit 2 and 4 seem not to be used but bit 3 has been
* seen _always_ set.
*
*
* Modem Status Register (MSR)
* ---------------------------
*
* BmRequestType: 0xc0 (1100 0000B)
* bRequest: 0x02
* wValue: 0x0000
* wIndex: 0x0000
* wLength: 0x0001
* Data: MSR (see below)
*
* Bit 7: Data Carrier Detect (CD). Reflects the state of the DCD line on the
* UART.
* Bit 6: Ring Indicator (RI). Reflects the state of the RI line on the UART.
* Bit 5: Data Set Ready (DSR). Reflects the state of the DSR line on the UART.
* Bit 4: Clear To Send (CTS). Reflects the state of the CTS line on the UART.
* Bit 3: Delta Data Carrier Detect (DDCD). Set to "1" if the -DCD line has
* changed state one more more times since the last time the MSR was
* read by the host.
* Bit 2: Trailing Edge Ring Indicator (TERI). Set to "1" if the -RI line has
* had a low to high transition since the last time the MSR was read by
* the host.
* Bit 1: Delta Data Set Ready (DDSR). Set to "1" if the -DSR line has changed
* state one more more times since the last time the MSR was read by the
* host.
* Bit 0: Delta Clear To Send (DCTS). Set to "1" if the -CTS line has changed
* state one more times since the last time the MSR was read by the
* host.
*
* SniffUSB observations: the MSR is also returned as first byte on the
* interrupt-in endpoint 0x83 to signal changes of modem status lines. The USB
* request to read MSR cannot be applied during normal device operation.
*
*
* Line Status Register (LSR)
* --------------------------
*
* Bit 7 Error in Receiver FIFO. On the 8250/16450 UART, this bit is zero.
* This bit is set to "1" when any of the bytes in the FIFO have one
* or more of the following error conditions: PE, FE, or BI.
* Bit 6 Transmitter Empty (TEMT). When set to "1", there are no words
* remaining in the transmit FIFO or the transmit shift register. The
* transmitter is completely idle.
* Bit 5 Transmitter Holding Register Empty (THRE). When set to "1", the
* FIFO (or holding register) now has room for at least one additional
* word to transmit. The transmitter may still be transmitting when
* this bit is set to "1".
* Bit 4 Break Interrupt (BI). The receiver has detected a Break signal.
* Bit 3 Framing Error (FE). A Start Bit was detected but the Stop Bit did
* not appear at the expected time. The received word is probably
* garbled.
* Bit 2 Parity Error (PE). The parity bit was incorrect for the word
* received.
* Bit 1 Overrun Error (OE). A new word was received and there was no room
* in the receive buffer. The newly-arrived word in the shift register
* is discarded. On 8250/16450 UARTs, the word in the holding register
* is discarded and the newly- arrived word is put in the holding
* register.
* Bit 0 Data Ready (DR). One or more words are in the receive FIFO that the
* host may read. A word must be completely received and moved from
* the shift register into the FIFO (or holding register for
* 8250/16450 designs) before this bit is set.
*
* SniffUSB observations: the LSR is returned as second byte on the
* interrupt-in endpoint 0x83 to signal error conditions. Such errors have
* been seen with minicom/zmodem transfers (CRC errors).
*
*
* Unknown #1
* -------------------
*
* BmRequestType: 0x40 (0100 0000B)
* bRequest: 0x0b
* wValue: 0x0000
* wIndex: 0x0000
* wLength: 0x0001
* Data: 0x00
*
* SniffUSB observations (Nov 2003): With the MCT-supplied Windows98 driver
* (U2SPORT.VXD, "File version: 1.21P.0104 for Win98/Me"), this request
* occurs immediately after a "Baud rate (divisor)" message. It was not
* observed at any other time. It is unclear what purpose this message
* serves.
*
*
* Unknown #2
* -------------------
*
* BmRequestType: 0x40 (0100 0000B)
* bRequest: 0x0c
* wValue: 0x0000
* wIndex: 0x0000
* wLength: 0x0001
* Data: 0x00
*
* SniffUSB observations (Nov 2003): With the MCT-supplied Windows98 driver
* (U2SPORT.VXD, "File version: 1.21P.0104 for Win98/Me"), this request
* occurs immediately after the 'Unknown #1' message (see above). It was
* not observed at any other time. It is unclear what other purpose (if
* any) this message might serve, but without it, the USB/RS-232 adapter
* will not write to RS-232 devices which do not assert the 'CTS' signal.
*
*
* Flow control
* ------------
*
* SniffUSB observations: no flow control specific requests have been realized
* apart from DTR/RTS settings. Both signals are dropped for no flow control
* but asserted for hardware or software flow control.
*
*
* Endpoint usage
* --------------
*
* SniffUSB observations: the bulk-out endpoint 0x1 and interrupt-in endpoint
* 0x81 is used to transmit and receive characters. The second interrupt-in
* endpoint 0x83 signals exceptional conditions like modem line changes and
* errors. The first byte returned is the MSR and the second byte the LSR.
*
*
* Other observations
* ------------------
*
* Queued bulk transfers like used in visor.c did not work.
*
*
* Properties of the USB device used (as found in /var/log/messages)
* -----------------------------------------------------------------
*
* Manufacturer: MCT Corporation.
* Product: USB-232 Interfact Controller
* SerialNumber: U2S22050
*
* Length = 18
* DescriptorType = 01
* USB version = 1.00
* Vendor:Product = 0711:0210
* MaxPacketSize0 = 8
* NumConfigurations = 1
* Device version = 1.02
* Device Class:SubClass:Protocol = 00:00:00
* Per-interface classes
* Configuration:
* bLength = 9
* bDescriptorType = 02
* wTotalLength = 0027
* bNumInterfaces = 01
* bConfigurationValue = 01
* iConfiguration = 00
* bmAttributes = c0
* MaxPower = 100mA
*
* Interface: 0
* Alternate Setting: 0
* bLength = 9
* bDescriptorType = 04
* bInterfaceNumber = 00
* bAlternateSetting = 00
* bNumEndpoints = 03
* bInterface Class:SubClass:Protocol = 00:00:00
* iInterface = 00
* Endpoint:
* bLength = 7
* bDescriptorType = 05
* bEndpointAddress = 81 (in)
* bmAttributes = 03 (Interrupt)
* wMaxPacketSize = 0040
* bInterval = 02
* Endpoint:
* bLength = 7
* bDescriptorType = 05
* bEndpointAddress = 01 (out)
* bmAttributes = 02 (Bulk)
* wMaxPacketSize = 0040
* bInterval = 00
* Endpoint:
* bLength = 7
* bDescriptorType = 05
* bEndpointAddress = 83 (in)
* bmAttributes = 03 (Interrupt)
* wMaxPacketSize = 0002
* bInterval = 02
*
*
* Hardware details (added by Martin Hamilton, 2001/12/06)
* -----------------------------------------------------------------
*
* This info was gleaned from opening a Belkin F5U109 DB9 USB serial
* adaptor, which turns out to simply be a re-badged U232-P9. We
* know this because there is a sticky label on the circuit board
* which says "U232-P9" ;-)
*
* The circuit board inside the adaptor contains a Philips PDIUSBD12
* USB endpoint chip and a Philips P87C52UBAA microcontroller with
* embedded UART. Exhaustive documentation for these is available at:
*
* http://www.semiconductors.philips.com/pip/p87c52ubaa
* http://www.nxp.com/acrobat_download/various/PDIUSBD12_PROGRAMMING_GUIDE.pdf
*
* Thanks to Julian Highfield for the pointer to the Philips database.
*
*/
#endif /* __LINUX_USB_SERIAL_MCT_U232_H */

View file

@ -0,0 +1,399 @@
/*
Some of this code is credited to Linux USB open source files that are
distributed with Linux.
Copyright: 2007 Metrologic Instruments. All rights reserved.
Copyright: 2011 Azimut Ltd. <http://azimutrzn.ru/>
*/
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb/serial.h>
#define DRIVER_DESC "Metrologic Instruments Inc. - USB-POS driver"
/* Product information. */
#define FOCUS_VENDOR_ID 0x0C2E
#define FOCUS_PRODUCT_ID_BI 0x0720
#define FOCUS_PRODUCT_ID_UNI 0x0700
#define METROUSB_SET_REQUEST_TYPE 0x40
#define METROUSB_SET_MODEM_CTRL_REQUEST 10
#define METROUSB_SET_BREAK_REQUEST 0x40
#define METROUSB_MCR_NONE 0x08 /* Deactivate DTR and RTS. */
#define METROUSB_MCR_RTS 0x0a /* Activate RTS. */
#define METROUSB_MCR_DTR 0x09 /* Activate DTR. */
#define WDR_TIMEOUT 5000 /* default urb timeout. */
/* Private data structure. */
struct metrousb_private {
spinlock_t lock;
int throttled;
unsigned long control_state;
};
/* Device table list. */
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_BI) },
{ USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_UNI) },
{ }, /* Terminating entry. */
};
MODULE_DEVICE_TABLE(usb, id_table);
/* UNI-Directional mode commands for device configure */
#define UNI_CMD_OPEN 0x80
#define UNI_CMD_CLOSE 0xFF
static inline int metrousb_is_unidirectional_mode(struct usb_serial_port *port)
{
__u16 product_id = le16_to_cpu(
port->serial->dev->descriptor.idProduct);
return product_id == FOCUS_PRODUCT_ID_UNI;
}
static int metrousb_send_unidirectional_cmd(u8 cmd, struct usb_serial_port *port)
{
int ret;
int actual_len;
u8 *buffer_cmd = NULL;
if (!metrousb_is_unidirectional_mode(port))
return 0;
buffer_cmd = kzalloc(sizeof(cmd), GFP_KERNEL);
if (!buffer_cmd)
return -ENOMEM;
*buffer_cmd = cmd;
ret = usb_interrupt_msg(port->serial->dev,
usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
buffer_cmd, sizeof(cmd),
&actual_len, USB_CTRL_SET_TIMEOUT);
kfree(buffer_cmd);
if (ret < 0)
return ret;
else if (actual_len != sizeof(cmd))
return -EIO;
return 0;
}
static void metrousb_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
int throttled = 0;
int result = 0;
unsigned long flags = 0;
dev_dbg(&port->dev, "%s\n", __func__);
switch (urb->status) {
case 0:
/* Success status, read from the port. */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* urb has been terminated. */
dev_dbg(&port->dev,
"%s - urb shutting down, error code=%d\n",
__func__, urb->status);
return;
default:
dev_dbg(&port->dev,
"%s - non-zero urb received, error code=%d\n",
__func__, urb->status);
goto exit;
}
/* Set the data read from the usb port into the serial port buffer. */
if (urb->actual_length) {
/* Loop through the data copying each byte to the tty layer. */
tty_insert_flip_string(&port->port, data, urb->actual_length);
/* Force the data to the tty layer. */
tty_flip_buffer_push(&port->port);
}
/* Set any port variables. */
spin_lock_irqsave(&metro_priv->lock, flags);
throttled = metro_priv->throttled;
spin_unlock_irqrestore(&metro_priv->lock, flags);
/* Continue trying to read if set. */
if (!throttled) {
usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
usb_rcvintpipe(port->serial->dev, port->interrupt_in_endpointAddress),
port->interrupt_in_urb->transfer_buffer,
port->interrupt_in_urb->transfer_buffer_length,
metrousb_read_int_callback, port, 1);
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
if (result)
dev_err(&port->dev,
"%s - failed submitting interrupt in urb, error code=%d\n",
__func__, result);
}
return;
exit:
/* Try to resubmit the urb. */
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
dev_err(&port->dev,
"%s - failed submitting interrupt in urb, error code=%d\n",
__func__, result);
}
static void metrousb_write_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
dev_warn(&port->dev, "%s not implemented yet.\n",
__func__);
}
static void metrousb_cleanup(struct usb_serial_port *port)
{
dev_dbg(&port->dev, "%s\n", __func__);
usb_unlink_urb(port->interrupt_in_urb);
usb_kill_urb(port->interrupt_in_urb);
metrousb_send_unidirectional_cmd(UNI_CMD_CLOSE, port);
}
static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
unsigned long flags = 0;
int result = 0;
dev_dbg(&port->dev, "%s\n", __func__);
/* Make sure the urb is initialized. */
if (!port->interrupt_in_urb) {
dev_err(&port->dev, "%s - interrupt urb not initialized\n",
__func__);
return -ENODEV;
}
/* Set the private data information for the port. */
spin_lock_irqsave(&metro_priv->lock, flags);
metro_priv->control_state = 0;
metro_priv->throttled = 0;
spin_unlock_irqrestore(&metro_priv->lock, flags);
/* Clear the urb pipe. */
usb_clear_halt(serial->dev, port->interrupt_in_urb->pipe);
/* Start reading from the device */
usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
port->interrupt_in_urb->transfer_buffer,
port->interrupt_in_urb->transfer_buffer_length,
metrousb_read_int_callback, port, 1);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result) {
dev_err(&port->dev,
"%s - failed submitting interrupt in urb, error code=%d\n",
__func__, result);
goto exit;
}
/* Send activate cmd to device */
result = metrousb_send_unidirectional_cmd(UNI_CMD_OPEN, port);
if (result) {
dev_err(&port->dev,
"%s - failed to configure device, error code=%d\n",
__func__, result);
goto exit;
}
dev_dbg(&port->dev, "%s - port open\n", __func__);
exit:
return result;
}
static int metrousb_set_modem_ctrl(struct usb_serial *serial, unsigned int control_state)
{
int retval = 0;
unsigned char mcr = METROUSB_MCR_NONE;
dev_dbg(&serial->dev->dev, "%s - control state = %d\n",
__func__, control_state);
/* Set the modem control value. */
if (control_state & TIOCM_DTR)
mcr |= METROUSB_MCR_DTR;
if (control_state & TIOCM_RTS)
mcr |= METROUSB_MCR_RTS;
/* Send the command to the usb port. */
retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
METROUSB_SET_REQUEST_TYPE, METROUSB_SET_MODEM_CTRL_REQUEST,
control_state, 0, NULL, 0, WDR_TIMEOUT);
if (retval < 0)
dev_err(&serial->dev->dev,
"%s - set modem ctrl=0x%x failed, error code=%d\n",
__func__, mcr, retval);
return retval;
}
static int metrousb_port_probe(struct usb_serial_port *port)
{
struct metrousb_private *metro_priv;
metro_priv = kzalloc(sizeof(*metro_priv), GFP_KERNEL);
if (!metro_priv)
return -ENOMEM;
spin_lock_init(&metro_priv->lock);
usb_set_serial_port_data(port, metro_priv);
return 0;
}
static int metrousb_port_remove(struct usb_serial_port *port)
{
struct metrousb_private *metro_priv;
metro_priv = usb_get_serial_port_data(port);
kfree(metro_priv);
return 0;
}
static void metrousb_throttle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
unsigned long flags = 0;
dev_dbg(tty->dev, "%s\n", __func__);
/* Set the private information for the port to stop reading data. */
spin_lock_irqsave(&metro_priv->lock, flags);
metro_priv->throttled = 1;
spin_unlock_irqrestore(&metro_priv->lock, flags);
}
static int metrousb_tiocmget(struct tty_struct *tty)
{
unsigned long control_state = 0;
struct usb_serial_port *port = tty->driver_data;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
unsigned long flags = 0;
dev_dbg(tty->dev, "%s\n", __func__);
spin_lock_irqsave(&metro_priv->lock, flags);
control_state = metro_priv->control_state;
spin_unlock_irqrestore(&metro_priv->lock, flags);
return control_state;
}
static int metrousb_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
unsigned long flags = 0;
unsigned long control_state = 0;
dev_dbg(tty->dev, "%s - set=%d, clear=%d\n", __func__, set, clear);
spin_lock_irqsave(&metro_priv->lock, flags);
control_state = metro_priv->control_state;
/* Set the RTS and DTR values. */
if (set & TIOCM_RTS)
control_state |= TIOCM_RTS;
if (set & TIOCM_DTR)
control_state |= TIOCM_DTR;
if (clear & TIOCM_RTS)
control_state &= ~TIOCM_RTS;
if (clear & TIOCM_DTR)
control_state &= ~TIOCM_DTR;
metro_priv->control_state = control_state;
spin_unlock_irqrestore(&metro_priv->lock, flags);
return metrousb_set_modem_ctrl(serial, control_state);
}
static void metrousb_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
unsigned long flags = 0;
int result = 0;
dev_dbg(tty->dev, "%s\n", __func__);
/* Set the private information for the port to resume reading data. */
spin_lock_irqsave(&metro_priv->lock, flags);
metro_priv->throttled = 0;
spin_unlock_irqrestore(&metro_priv->lock, flags);
/* Submit the urb to read from the port. */
port->interrupt_in_urb->dev = port->serial->dev;
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
if (result)
dev_err(tty->dev,
"failed submitting interrupt in urb error code=%d\n",
result);
}
static struct usb_serial_driver metrousb_device = {
.driver = {
.owner = THIS_MODULE,
.name = "metro-usb",
},
.description = "Metrologic USB to Serial",
.id_table = id_table,
.num_ports = 1,
.open = metrousb_open,
.close = metrousb_cleanup,
.read_int_callback = metrousb_read_int_callback,
.write_int_callback = metrousb_write_int_callback,
.port_probe = metrousb_port_probe,
.port_remove = metrousb_port_remove,
.throttle = metrousb_throttle,
.unthrottle = metrousb_unthrottle,
.tiocmget = metrousb_tiocmget,
.tiocmset = metrousb_tiocmset,
};
static struct usb_serial_driver * const serial_drivers[] = {
&metrousb_device,
NULL,
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Philip Nicastro");
MODULE_AUTHOR("Aleksey Babahin <tamerlan311@gmail.com>");
MODULE_DESCRIPTION(DRIVER_DESC);

2064
drivers/usb/serial/mos7720.c Normal file

File diff suppressed because it is too large Load diff

2470
drivers/usb/serial/mos7840.c Normal file

File diff suppressed because it is too large Load diff

1394
drivers/usb/serial/mxuport.c Normal file

File diff suppressed because it is too large Load diff

118
drivers/usb/serial/navman.c Normal file
View file

@ -0,0 +1,118 @@
/*
* Navman Serial USB driver
*
* Copyright (C) 2006 Greg Kroah-Hartman <gregkh@suse.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* TODO:
* Add termios method that uses copy_hw but also kills all echo
* flags as the navman is rx only so cannot echo.
*/
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x0a99, 0x0001) }, /* Talon Technology device */
{ USB_DEVICE(0x0df7, 0x0900) }, /* Mobile Action i-gotU */
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
static void navman_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
int status = urb->status;
int result;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
__func__, status);
goto exit;
}
usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
if (urb->actual_length) {
tty_insert_flip_string(&port->port, data, urb->actual_length);
tty_flip_buffer_push(&port->port);
}
exit:
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
dev_err(&urb->dev->dev,
"%s - Error %d submitting interrupt urb\n",
__func__, result);
}
static int navman_open(struct tty_struct *tty, struct usb_serial_port *port)
{
int result = 0;
if (port->interrupt_in_urb) {
dev_dbg(&port->dev, "%s - adding interrupt input for treo\n",
__func__);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result)
dev_err(&port->dev,
"%s - failed submitting interrupt urb, error %d\n",
__func__, result);
}
return result;
}
static void navman_close(struct usb_serial_port *port)
{
usb_kill_urb(port->interrupt_in_urb);
}
static int navman_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
{
/*
* This device can't write any data, only read from the device
*/
return -EOPNOTSUPP;
}
static struct usb_serial_driver navman_device = {
.driver = {
.owner = THIS_MODULE,
.name = "navman",
},
.id_table = id_table,
.num_ports = 1,
.open = navman_open,
.close = navman_close,
.write = navman_write,
.read_int_callback = navman_read_int_callback,
};
static struct usb_serial_driver * const serial_drivers[] = {
&navman_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,258 @@
/*
* USB ZyXEL omni.net LCD PLUS driver
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver
*
* Please report both successes and troubles to the author at omninet@kroah.com
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#define DRIVER_AUTHOR "Alessandro Zummo"
#define DRIVER_DESC "USB ZyXEL omni.net LCD PLUS Driver"
#define ZYXEL_VENDOR_ID 0x0586
#define ZYXEL_OMNINET_ID 0x1000
/* This one seems to be a re-branded ZyXEL device */
#define BT_IGNITIONPRO_ID 0x2000
/* function prototypes */
static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port);
static void omninet_process_read_urb(struct urb *urb);
static void omninet_write_bulk_callback(struct urb *urb);
static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static int omninet_write_room(struct tty_struct *tty);
static void omninet_disconnect(struct usb_serial *serial);
static int omninet_port_probe(struct usb_serial_port *port);
static int omninet_port_remove(struct usb_serial_port *port);
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
{ USB_DEVICE(ZYXEL_VENDOR_ID, BT_IGNITIONPRO_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver zyxel_omninet_device = {
.driver = {
.owner = THIS_MODULE,
.name = "omninet",
},
.description = "ZyXEL - omni.net lcd plus usb",
.id_table = id_table,
.num_ports = 1,
.port_probe = omninet_port_probe,
.port_remove = omninet_port_remove,
.open = omninet_open,
.write = omninet_write,
.write_room = omninet_write_room,
.write_bulk_callback = omninet_write_bulk_callback,
.process_read_urb = omninet_process_read_urb,
.disconnect = omninet_disconnect,
};
static struct usb_serial_driver * const serial_drivers[] = {
&zyxel_omninet_device, NULL
};
/*
* The protocol.
*
* The omni.net always exchange 64 bytes of data with the host. The first
* four bytes are the control header.
*
* oh_seq is a sequence number. Don't know if/how it's used.
* oh_len is the length of the data bytes in the packet.
* oh_xxx Bit-mapped, related to handshaking and status info.
* I normally set it to 0x03 in transmitted frames.
* 7: Active when the TA is in a CONNECTed state.
* 6: unknown
* 5: handshaking, unknown
* 4: handshaking, unknown
* 3: unknown, usually 0
* 2: unknown, usually 0
* 1: handshaking, unknown, usually set to 1 in transmitted frames
* 0: handshaking, unknown, usually set to 1 in transmitted frames
* oh_pad Probably a pad byte.
*
* After the header you will find data bytes if oh_len was greater than zero.
*/
struct omninet_header {
__u8 oh_seq;
__u8 oh_len;
__u8 oh_xxx;
__u8 oh_pad;
};
struct omninet_data {
__u8 od_outseq; /* Sequence number for bulk_out URBs */
};
static int omninet_port_probe(struct usb_serial_port *port)
{
struct omninet_data *od;
od = kzalloc(sizeof(*od), GFP_KERNEL);
if (!od)
return -ENOMEM;
usb_set_serial_port_data(port, od);
return 0;
}
static int omninet_port_remove(struct usb_serial_port *port)
{
struct omninet_data *od;
od = usb_get_serial_port_data(port);
kfree(od);
return 0;
}
static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct usb_serial_port *wport;
wport = serial->port[1];
tty_port_tty_set(&wport->port, tty);
return usb_serial_generic_open(tty, port);
}
#define OMNINET_HEADERLEN 4
#define OMNINET_BULKOUTSIZE 64
#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN)
static void omninet_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
const struct omninet_header *hdr = urb->transfer_buffer;
const unsigned char *data;
size_t data_len;
if (urb->actual_length <= OMNINET_HEADERLEN || !hdr->oh_len)
return;
data = (char *)urb->transfer_buffer + OMNINET_HEADERLEN;
data_len = min_t(size_t, urb->actual_length - OMNINET_HEADERLEN,
hdr->oh_len);
tty_insert_flip_string(&port->port, data, data_len);
tty_flip_buffer_push(&port->port);
}
static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
{
struct usb_serial *serial = port->serial;
struct usb_serial_port *wport = serial->port[1];
struct omninet_data *od = usb_get_serial_port_data(port);
struct omninet_header *header = (struct omninet_header *)
wport->write_urb->transfer_buffer;
int result;
if (count == 0) {
dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
return 0;
}
if (!test_and_clear_bit(0, &port->write_urbs_free)) {
dev_dbg(&port->dev, "%s - already writing\n", __func__);
return 0;
}
count = (count > OMNINET_PAYLOADSIZE) ? OMNINET_PAYLOADSIZE : count;
memcpy(wport->write_urb->transfer_buffer + OMNINET_HEADERLEN,
buf, count);
usb_serial_debug_data(&port->dev, __func__, count,
wport->write_urb->transfer_buffer);
header->oh_seq = od->od_outseq++;
header->oh_len = count;
header->oh_xxx = 0x03;
header->oh_pad = 0x00;
/* send the data out the bulk port, always 64 bytes */
wport->write_urb->transfer_buffer_length = OMNINET_BULKOUTSIZE;
result = usb_submit_urb(wport->write_urb, GFP_ATOMIC);
if (result) {
set_bit(0, &wport->write_urbs_free);
dev_err_console(port,
"%s - failed submitting write urb, error %d\n",
__func__, result);
} else
result = count;
return result;
}
static int omninet_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
struct usb_serial_port *wport = serial->port[1];
int room = 0; /* Default: no room */
if (test_bit(0, &wport->write_urbs_free))
room = wport->bulk_out_size - OMNINET_HEADERLEN;
dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
return room;
}
static void omninet_write_bulk_callback(struct urb *urb)
{
/* struct omninet_header *header = (struct omninet_header *)
urb->transfer_buffer; */
struct usb_serial_port *port = urb->context;
int status = urb->status;
set_bit(0, &port->write_urbs_free);
if (status) {
dev_dbg(&port->dev, "%s - nonzero write bulk status received: %d\n",
__func__, status);
return;
}
usb_serial_port_softint(port);
}
static void omninet_disconnect(struct usb_serial *serial)
{
struct usb_serial_port *wport = serial->port[1];
usb_kill_urb(wport->write_urb);
}
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,437 @@
/*
* Opticon USB barcode to serial driver
*
* Copyright (C) 2011 - 2012 Johan Hovold <jhovold@gmail.com>
* Copyright (C) 2011 Martin Jansen <martin.jansen@opticon.com>
* Copyright (C) 2008 - 2009 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (C) 2008 - 2009 Novell Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/slab.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
#define CONTROL_RTS 0x02
#define RESEND_CTS_STATE 0x03
/* max number of write urbs in flight */
#define URB_UPPER_LIMIT 8
/* This driver works for the Opticon 1D barcode reader
* an examples of 1D barcode types are EAN, UPC, Code39, IATA etc.. */
#define DRIVER_DESC "Opticon USB barcode to serial driver (1D)"
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x065a, 0x0009) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
/* This structure holds all of the individual device information */
struct opticon_private {
spinlock_t lock; /* protects the following flags */
bool rts;
bool cts;
int outstanding_urbs;
};
static void opticon_process_data_packet(struct usb_serial_port *port,
const unsigned char *buf, size_t len)
{
tty_insert_flip_string(&port->port, buf, len);
tty_flip_buffer_push(&port->port);
}
static void opticon_process_status_packet(struct usb_serial_port *port,
const unsigned char *buf, size_t len)
{
struct opticon_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (buf[0] == 0x00)
priv->cts = false;
else
priv->cts = true;
spin_unlock_irqrestore(&priv->lock, flags);
}
static void opticon_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
const unsigned char *hdr = urb->transfer_buffer;
const unsigned char *data = hdr + 2;
size_t data_len = urb->actual_length - 2;
if (urb->actual_length <= 2) {
dev_dbg(&port->dev, "malformed packet received: %d bytes\n",
urb->actual_length);
return;
}
/*
* Data from the device comes with a 2 byte header:
*
* <0x00><0x00>data...
* This is real data to be sent to the tty layer
* <0x00><0x01>level
* This is a CTS level change, the third byte is the CTS
* value (0 for low, 1 for high).
*/
if ((hdr[0] == 0x00) && (hdr[1] == 0x00)) {
opticon_process_data_packet(port, data, data_len);
} else if ((hdr[0] == 0x00) && (hdr[1] == 0x01)) {
opticon_process_status_packet(port, data, data_len);
} else {
dev_dbg(&port->dev, "unknown packet received: %02x %02x\n",
hdr[0], hdr[1]);
}
}
static int send_control_msg(struct usb_serial_port *port, u8 requesttype,
u8 val)
{
struct usb_serial *serial = port->serial;
int retval;
u8 *buffer;
buffer = kzalloc(1, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
buffer[0] = val;
/* Send the message to the vendor control endpoint
* of the connected device */
retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
requesttype,
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
0, 0, buffer, 1, 0);
kfree(buffer);
if (retval < 0)
return retval;
return 0;
}
static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct opticon_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
int res;
spin_lock_irqsave(&priv->lock, flags);
priv->rts = false;
spin_unlock_irqrestore(&priv->lock, flags);
/* Clear RTS line */
send_control_msg(port, CONTROL_RTS, 0);
/* clear the halt status of the endpoint */
usb_clear_halt(port->serial->dev, port->read_urb->pipe);
res = usb_serial_generic_open(tty, port);
if (!res)
return res;
/* Request CTS line state, sometimes during opening the current
* CTS state can be missed. */
send_control_msg(port, RESEND_CTS_STATE, 1);
return res;
}
static void opticon_write_control_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct opticon_private *priv = usb_get_serial_port_data(port);
int status = urb->status;
unsigned long flags;
/* free up the transfer buffer, as usb_free_urb() does not do this */
kfree(urb->transfer_buffer);
/* setup packet may be set if we're using it for writing */
kfree(urb->setup_packet);
if (status)
dev_dbg(&port->dev,
"%s - non-zero urb status received: %d\n",
__func__, status);
spin_lock_irqsave(&priv->lock, flags);
--priv->outstanding_urbs;
spin_unlock_irqrestore(&priv->lock, flags);
usb_serial_port_softint(port);
}
static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
{
struct opticon_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
struct urb *urb;
unsigned char *buffer;
unsigned long flags;
int status;
struct usb_ctrlrequest *dr;
spin_lock_irqsave(&priv->lock, flags);
if (priv->outstanding_urbs > URB_UPPER_LIMIT) {
spin_unlock_irqrestore(&priv->lock, flags);
dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
return 0;
}
priv->outstanding_urbs++;
spin_unlock_irqrestore(&priv->lock, flags);
buffer = kmalloc(count, GFP_ATOMIC);
if (!buffer) {
count = -ENOMEM;
goto error_no_buffer;
}
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
count = -ENOMEM;
goto error_no_urb;
}
memcpy(buffer, buf, count);
usb_serial_debug_data(&port->dev, __func__, count, buffer);
/* The connected devices do not have a bulk write endpoint,
* to transmit data to de barcode device the control endpoint is used */
dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
if (!dr) {
count = -ENOMEM;
goto error_no_dr;
}
dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT;
dr->bRequest = 0x01;
dr->wValue = 0;
dr->wIndex = 0;
dr->wLength = cpu_to_le16(count);
usb_fill_control_urb(urb, serial->dev,
usb_sndctrlpipe(serial->dev, 0),
(unsigned char *)dr, buffer, count,
opticon_write_control_callback, port);
/* send it down the pipe */
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status) {
dev_err(&port->dev,
"%s - usb_submit_urb(write endpoint) failed status = %d\n",
__func__, status);
count = status;
goto error;
}
/* we are done with this urb, so let the host driver
* really free it when it is finished with it */
usb_free_urb(urb);
return count;
error:
kfree(dr);
error_no_dr:
usb_free_urb(urb);
error_no_urb:
kfree(buffer);
error_no_buffer:
spin_lock_irqsave(&priv->lock, flags);
--priv->outstanding_urbs;
spin_unlock_irqrestore(&priv->lock, flags);
return count;
}
static int opticon_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct opticon_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
/*
* We really can take almost anything the user throws at us
* but let's pick a nice big number to tell the tty
* layer that we have lots of free space, unless we don't.
*/
spin_lock_irqsave(&priv->lock, flags);
if (priv->outstanding_urbs > URB_UPPER_LIMIT * 2 / 3) {
spin_unlock_irqrestore(&priv->lock, flags);
dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
return 0;
}
spin_unlock_irqrestore(&priv->lock, flags);
return 2048;
}
static int opticon_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct opticon_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
int result = 0;
spin_lock_irqsave(&priv->lock, flags);
if (priv->rts)
result |= TIOCM_RTS;
if (priv->cts)
result |= TIOCM_CTS;
spin_unlock_irqrestore(&priv->lock, flags);
dev_dbg(&port->dev, "%s - %x\n", __func__, result);
return result;
}
static int opticon_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct opticon_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
bool rts;
bool changed = false;
int ret;
/* We only support RTS so we only handle that */
spin_lock_irqsave(&priv->lock, flags);
rts = priv->rts;
if (set & TIOCM_RTS)
priv->rts = true;
if (clear & TIOCM_RTS)
priv->rts = false;
changed = rts ^ priv->rts;
spin_unlock_irqrestore(&priv->lock, flags);
if (!changed)
return 0;
ret = send_control_msg(port, CONTROL_RTS, !rts);
if (ret)
return usb_translate_errors(ret);
return 0;
}
static int get_serial_info(struct usb_serial_port *port,
struct serial_struct __user *serial)
{
struct serial_struct tmp;
if (!serial)
return -EFAULT;
memset(&tmp, 0x00, sizeof(tmp));
/* fake emulate a 16550 uart to make userspace code happy */
tmp.type = PORT_16550A;
tmp.line = port->minor;
tmp.port = 0;
tmp.irq = 0;
tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = 1024;
tmp.baud_base = 9600;
tmp.close_delay = 5*HZ;
tmp.closing_wait = 30*HZ;
if (copy_to_user(serial, &tmp, sizeof(*serial)))
return -EFAULT;
return 0;
}
static int opticon_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
switch (cmd) {
case TIOCGSERIAL:
return get_serial_info(port,
(struct serial_struct __user *)arg);
}
return -ENOIOCTLCMD;
}
static int opticon_startup(struct usb_serial *serial)
{
if (!serial->num_bulk_in) {
dev_err(&serial->dev->dev, "no bulk in endpoint\n");
return -ENODEV;
}
return 0;
}
static int opticon_port_probe(struct usb_serial_port *port)
{
struct opticon_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
usb_set_serial_port_data(port, priv);
return 0;
}
static int opticon_port_remove(struct usb_serial_port *port)
{
struct opticon_private *priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static struct usb_serial_driver opticon_device = {
.driver = {
.owner = THIS_MODULE,
.name = "opticon",
},
.id_table = id_table,
.num_ports = 1,
.bulk_in_size = 256,
.attach = opticon_startup,
.port_probe = opticon_port_probe,
.port_remove = opticon_port_remove,
.open = opticon_open,
.write = opticon_write,
.write_room = opticon_write_room,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
.ioctl = opticon_ioctl,
.tiocmget = opticon_tiocmget,
.tiocmset = opticon_tiocmset,
.process_read_urb = opticon_process_read_urb,
};
static struct usb_serial_driver * const serial_drivers[] = {
&opticon_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

2021
drivers/usb/serial/option.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,847 @@
/*
* Ours Technology Inc. OTi-6858 USB to serial adapter driver.
*
* Copyleft (C) 2007 Kees Lemmens (adapted for kernel 2.6.20)
* Copyright (C) 2006 Tomasz Michal Lukaszewski (FIXME: add e-mail)
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2003 IBM Corp.
*
* Many thanks to the authors of pl2303 driver: all functions in this file
* are heavily based on pl2303 code, buffering code is a 1-to-1 copy.
*
* Warning! You use this driver on your own risk! The only official
* description of this device I have is datasheet from manufacturer,
* and it doesn't contain almost any information needed to write a driver.
* Almost all knowlegde used while writing this driver was gathered by:
* - analyzing traffic between device and the M$ Windows 2000 driver,
* - trying different bit combinations and checking pin states
* with a voltmeter,
* - receiving malformed frames and producing buffer overflows
* to learn how errors are reported,
* So, THIS CODE CAN DESTROY OTi-6858 AND ANY OTHER DEVICES, THAT ARE
* CONNECTED TO IT!
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver
*
* TODO:
* - implement correct flushing for ioctls and oti6858_close()
* - check how errors (rx overflow, parity error, framing error) are reported
* - implement oti6858_break_ctl()
* - implement more ioctls
* - test/implement flow control
* - allow setting custom baud rates
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
#include <linux/kfifo.h>
#include "oti6858.h"
#define OTI6858_DESCRIPTION \
"Ours Technology Inc. OTi-6858 USB to serial adapter driver"
#define OTI6858_AUTHOR "Tomasz Michal Lukaszewski <FIXME@FIXME>"
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(OTI6858_VENDOR_ID, OTI6858_PRODUCT_ID) },
{ }
};
MODULE_DEVICE_TABLE(usb, id_table);
/* requests */
#define OTI6858_REQ_GET_STATUS (USB_DIR_IN | USB_TYPE_VENDOR | 0x00)
#define OTI6858_REQ_T_GET_STATUS 0x01
#define OTI6858_REQ_SET_LINE (USB_DIR_OUT | USB_TYPE_VENDOR | 0x00)
#define OTI6858_REQ_T_SET_LINE 0x00
#define OTI6858_REQ_CHECK_TXBUFF (USB_DIR_IN | USB_TYPE_VENDOR | 0x01)
#define OTI6858_REQ_T_CHECK_TXBUFF 0x00
/* format of the control packet */
struct oti6858_control_pkt {
__le16 divisor; /* baud rate = 96000000 / (16 * divisor), LE */
#define OTI6858_MAX_BAUD_RATE 3000000
u8 frame_fmt;
#define FMT_STOP_BITS_MASK 0xc0
#define FMT_STOP_BITS_1 0x00
#define FMT_STOP_BITS_2 0x40 /* 1.5 stop bits if FMT_DATA_BITS_5 */
#define FMT_PARITY_MASK 0x38
#define FMT_PARITY_NONE 0x00
#define FMT_PARITY_ODD 0x08
#define FMT_PARITY_EVEN 0x18
#define FMT_PARITY_MARK 0x28
#define FMT_PARITY_SPACE 0x38
#define FMT_DATA_BITS_MASK 0x03
#define FMT_DATA_BITS_5 0x00
#define FMT_DATA_BITS_6 0x01
#define FMT_DATA_BITS_7 0x02
#define FMT_DATA_BITS_8 0x03
u8 something; /* always equals 0x43 */
u8 control; /* settings of flow control lines */
#define CONTROL_MASK 0x0c
#define CONTROL_DTR_HIGH 0x08
#define CONTROL_RTS_HIGH 0x04
u8 tx_status;
#define TX_BUFFER_EMPTIED 0x09
u8 pin_state;
#define PIN_MASK 0x3f
#define PIN_MSR_MASK 0x1b
#define PIN_RTS 0x20 /* output pin */
#define PIN_CTS 0x10 /* input pin, active low */
#define PIN_DSR 0x08 /* input pin, active low */
#define PIN_DTR 0x04 /* output pin */
#define PIN_RI 0x02 /* input pin, active low */
#define PIN_DCD 0x01 /* input pin, active low */
u8 rx_bytes_avail; /* number of bytes in rx buffer */;
};
#define OTI6858_CTRL_PKT_SIZE sizeof(struct oti6858_control_pkt)
#define OTI6858_CTRL_EQUALS_PENDING(a, priv) \
(((a)->divisor == (priv)->pending_setup.divisor) \
&& ((a)->control == (priv)->pending_setup.control) \
&& ((a)->frame_fmt == (priv)->pending_setup.frame_fmt))
/* function prototypes */
static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port);
static void oti6858_close(struct usb_serial_port *port);
static void oti6858_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old);
static void oti6858_init_termios(struct tty_struct *tty);
static void oti6858_read_int_callback(struct urb *urb);
static void oti6858_read_bulk_callback(struct urb *urb);
static void oti6858_write_bulk_callback(struct urb *urb);
static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static int oti6858_write_room(struct tty_struct *tty);
static int oti6858_chars_in_buffer(struct tty_struct *tty);
static int oti6858_tiocmget(struct tty_struct *tty);
static int oti6858_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
static int oti6858_port_probe(struct usb_serial_port *port);
static int oti6858_port_remove(struct usb_serial_port *port);
/* device info */
static struct usb_serial_driver oti6858_device = {
.driver = {
.owner = THIS_MODULE,
.name = "oti6858",
},
.id_table = id_table,
.num_ports = 1,
.open = oti6858_open,
.close = oti6858_close,
.write = oti6858_write,
.set_termios = oti6858_set_termios,
.init_termios = oti6858_init_termios,
.tiocmget = oti6858_tiocmget,
.tiocmset = oti6858_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.read_bulk_callback = oti6858_read_bulk_callback,
.read_int_callback = oti6858_read_int_callback,
.write_bulk_callback = oti6858_write_bulk_callback,
.write_room = oti6858_write_room,
.chars_in_buffer = oti6858_chars_in_buffer,
.port_probe = oti6858_port_probe,
.port_remove = oti6858_port_remove,
};
static struct usb_serial_driver * const serial_drivers[] = {
&oti6858_device, NULL
};
struct oti6858_private {
spinlock_t lock;
struct oti6858_control_pkt status;
struct {
u8 read_urb_in_use;
u8 write_urb_in_use;
} flags;
struct delayed_work delayed_write_work;
struct {
__le16 divisor;
u8 frame_fmt;
u8 control;
} pending_setup;
u8 transient;
u8 setup_done;
struct delayed_work delayed_setup_work;
struct usb_serial_port *port; /* USB port with which associated */
};
static void setup_line(struct work_struct *work)
{
struct oti6858_private *priv = container_of(work,
struct oti6858_private, delayed_setup_work.work);
struct usb_serial_port *port = priv->port;
struct oti6858_control_pkt *new_setup;
unsigned long flags;
int result;
new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL);
if (!new_setup) {
/* we will try again */
schedule_delayed_work(&priv->delayed_setup_work,
msecs_to_jiffies(2));
return;
}
result = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
OTI6858_REQ_T_GET_STATUS,
OTI6858_REQ_GET_STATUS,
0, 0,
new_setup, OTI6858_CTRL_PKT_SIZE,
100);
if (result != OTI6858_CTRL_PKT_SIZE) {
dev_err(&port->dev, "%s(): error reading status\n", __func__);
kfree(new_setup);
/* we will try again */
schedule_delayed_work(&priv->delayed_setup_work,
msecs_to_jiffies(2));
return;
}
spin_lock_irqsave(&priv->lock, flags);
if (!OTI6858_CTRL_EQUALS_PENDING(new_setup, priv)) {
new_setup->divisor = priv->pending_setup.divisor;
new_setup->control = priv->pending_setup.control;
new_setup->frame_fmt = priv->pending_setup.frame_fmt;
spin_unlock_irqrestore(&priv->lock, flags);
result = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
OTI6858_REQ_T_SET_LINE,
OTI6858_REQ_SET_LINE,
0, 0,
new_setup, OTI6858_CTRL_PKT_SIZE,
100);
} else {
spin_unlock_irqrestore(&priv->lock, flags);
result = 0;
}
kfree(new_setup);
spin_lock_irqsave(&priv->lock, flags);
if (result != OTI6858_CTRL_PKT_SIZE)
priv->transient = 0;
priv->setup_done = 1;
spin_unlock_irqrestore(&priv->lock, flags);
dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result != 0) {
dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n",
__func__, result);
}
}
static void send_data(struct work_struct *work)
{
struct oti6858_private *priv = container_of(work,
struct oti6858_private, delayed_write_work.work);
struct usb_serial_port *port = priv->port;
int count = 0, result;
unsigned long flags;
u8 *allow;
spin_lock_irqsave(&priv->lock, flags);
if (priv->flags.write_urb_in_use) {
spin_unlock_irqrestore(&priv->lock, flags);
schedule_delayed_work(&priv->delayed_write_work,
msecs_to_jiffies(2));
return;
}
priv->flags.write_urb_in_use = 1;
spin_unlock_irqrestore(&priv->lock, flags);
spin_lock_irqsave(&port->lock, flags);
count = kfifo_len(&port->write_fifo);
spin_unlock_irqrestore(&port->lock, flags);
if (count > port->bulk_out_size)
count = port->bulk_out_size;
if (count != 0) {
allow = kmalloc(1, GFP_KERNEL);
if (!allow)
return;
result = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
OTI6858_REQ_T_CHECK_TXBUFF,
OTI6858_REQ_CHECK_TXBUFF,
count, 0, allow, 1, 100);
if (result != 1 || *allow != 0)
count = 0;
kfree(allow);
}
if (count == 0) {
priv->flags.write_urb_in_use = 0;
dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
result = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
if (result != 0) {
dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n",
__func__, result);
}
return;
}
count = kfifo_out_locked(&port->write_fifo,
port->write_urb->transfer_buffer,
count, &port->lock);
port->write_urb->transfer_buffer_length = count;
result = usb_submit_urb(port->write_urb, GFP_NOIO);
if (result != 0) {
dev_err_console(port, "%s(): usb_submit_urb() failed with error %d\n",
__func__, result);
priv->flags.write_urb_in_use = 0;
}
usb_serial_port_softint(port);
}
static int oti6858_port_probe(struct usb_serial_port *port)
{
struct oti6858_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
priv->port = port;
INIT_DELAYED_WORK(&priv->delayed_setup_work, setup_line);
INIT_DELAYED_WORK(&priv->delayed_write_work, send_data);
usb_set_serial_port_data(port, priv);
port->port.drain_delay = 256; /* FIXME: check the FIFO length */
return 0;
}
static int oti6858_port_remove(struct usb_serial_port *port)
{
struct oti6858_private *priv;
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
{
if (!count)
return count;
count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
return count;
}
static int oti6858_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
int room = 0;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
room = kfifo_avail(&port->write_fifo);
spin_unlock_irqrestore(&port->lock, flags);
return room;
}
static int oti6858_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
int chars = 0;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
chars = kfifo_len(&port->write_fifo);
spin_unlock_irqrestore(&port->lock, flags);
return chars;
}
static void oti6858_init_termios(struct tty_struct *tty)
{
tty->termios = tty_std_termios;
tty->termios.c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL;
tty->termios.c_ispeed = 38400;
tty->termios.c_ospeed = 38400;
}
static void oti6858_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct oti6858_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
unsigned int cflag;
u8 frame_fmt, control;
__le16 divisor;
int br;
cflag = tty->termios.c_cflag;
spin_lock_irqsave(&priv->lock, flags);
divisor = priv->pending_setup.divisor;
frame_fmt = priv->pending_setup.frame_fmt;
control = priv->pending_setup.control;
spin_unlock_irqrestore(&priv->lock, flags);
frame_fmt &= ~FMT_DATA_BITS_MASK;
switch (cflag & CSIZE) {
case CS5:
frame_fmt |= FMT_DATA_BITS_5;
break;
case CS6:
frame_fmt |= FMT_DATA_BITS_6;
break;
case CS7:
frame_fmt |= FMT_DATA_BITS_7;
break;
default:
case CS8:
frame_fmt |= FMT_DATA_BITS_8;
break;
}
/* manufacturer claims that this device can work with baud rates
* up to 3 Mbps; I've tested it only on 115200 bps, so I can't
* guarantee that any other baud rate will work (especially
* the higher ones)
*/
br = tty_get_baud_rate(tty);
if (br == 0) {
divisor = 0;
} else {
int real_br;
int new_divisor;
br = min(br, OTI6858_MAX_BAUD_RATE);
new_divisor = (96000000 + 8 * br) / (16 * br);
real_br = 96000000 / (16 * new_divisor);
divisor = cpu_to_le16(new_divisor);
tty_encode_baud_rate(tty, real_br, real_br);
}
frame_fmt &= ~FMT_STOP_BITS_MASK;
if ((cflag & CSTOPB) != 0)
frame_fmt |= FMT_STOP_BITS_2;
else
frame_fmt |= FMT_STOP_BITS_1;
frame_fmt &= ~FMT_PARITY_MASK;
if ((cflag & PARENB) != 0) {
if ((cflag & PARODD) != 0)
frame_fmt |= FMT_PARITY_ODD;
else
frame_fmt |= FMT_PARITY_EVEN;
} else {
frame_fmt |= FMT_PARITY_NONE;
}
control &= ~CONTROL_MASK;
if ((cflag & CRTSCTS) != 0)
control |= (CONTROL_DTR_HIGH | CONTROL_RTS_HIGH);
/* change control lines if we are switching to or from B0 */
/* FIXME:
spin_lock_irqsave(&priv->lock, flags);
control = priv->line_control;
if ((cflag & CBAUD) == B0)
priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
else
priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
if (control != priv->line_control) {
control = priv->line_control;
spin_unlock_irqrestore(&priv->lock, flags);
set_control_lines(serial->dev, control);
} else {
spin_unlock_irqrestore(&priv->lock, flags);
}
*/
spin_lock_irqsave(&priv->lock, flags);
if (divisor != priv->pending_setup.divisor
|| control != priv->pending_setup.control
|| frame_fmt != priv->pending_setup.frame_fmt) {
priv->pending_setup.divisor = divisor;
priv->pending_setup.control = control;
priv->pending_setup.frame_fmt = frame_fmt;
}
spin_unlock_irqrestore(&priv->lock, flags);
}
static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct oti6858_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
struct oti6858_control_pkt *buf;
unsigned long flags;
int result;
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
buf = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
OTI6858_REQ_T_GET_STATUS,
OTI6858_REQ_GET_STATUS,
0, 0,
buf, OTI6858_CTRL_PKT_SIZE,
100);
if (result != OTI6858_CTRL_PKT_SIZE) {
/* assume default (after power-on reset) values */
buf->divisor = cpu_to_le16(0x009c); /* 38400 bps */
buf->frame_fmt = 0x03; /* 8N1 */
buf->something = 0x43;
buf->control = 0x4c; /* DTR, RTS */
buf->tx_status = 0x00;
buf->pin_state = 0x5b; /* RTS, CTS, DSR, DTR, RI, DCD */
buf->rx_bytes_avail = 0x00;
}
spin_lock_irqsave(&priv->lock, flags);
memcpy(&priv->status, buf, OTI6858_CTRL_PKT_SIZE);
priv->pending_setup.divisor = buf->divisor;
priv->pending_setup.frame_fmt = buf->frame_fmt;
priv->pending_setup.control = buf->control;
spin_unlock_irqrestore(&priv->lock, flags);
kfree(buf);
dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result != 0) {
dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n",
__func__, result);
oti6858_close(port);
return result;
}
/* setup termios */
if (tty)
oti6858_set_termios(tty, port, NULL);
return 0;
}
static void oti6858_close(struct usb_serial_port *port)
{
struct oti6858_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
/* clear out any remaining data in the buffer */
kfifo_reset_out(&port->write_fifo);
spin_unlock_irqrestore(&port->lock, flags);
dev_dbg(&port->dev, "%s(): after buf_clear()\n", __func__);
/* cancel scheduled setup */
cancel_delayed_work_sync(&priv->delayed_setup_work);
cancel_delayed_work_sync(&priv->delayed_write_work);
/* shutdown our urbs */
dev_dbg(&port->dev, "%s(): shutting down urbs\n", __func__);
usb_kill_urb(port->write_urb);
usb_kill_urb(port->read_urb);
usb_kill_urb(port->interrupt_in_urb);
}
static int oti6858_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct oti6858_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
u8 control;
dev_dbg(&port->dev, "%s(set = 0x%08x, clear = 0x%08x)\n",
__func__, set, clear);
/* FIXME: check if this is correct (active high/low) */
spin_lock_irqsave(&priv->lock, flags);
control = priv->pending_setup.control;
if ((set & TIOCM_RTS) != 0)
control |= CONTROL_RTS_HIGH;
if ((set & TIOCM_DTR) != 0)
control |= CONTROL_DTR_HIGH;
if ((clear & TIOCM_RTS) != 0)
control &= ~CONTROL_RTS_HIGH;
if ((clear & TIOCM_DTR) != 0)
control &= ~CONTROL_DTR_HIGH;
if (control != priv->pending_setup.control)
priv->pending_setup.control = control;
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int oti6858_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct oti6858_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
unsigned pin_state;
unsigned result = 0;
spin_lock_irqsave(&priv->lock, flags);
pin_state = priv->status.pin_state & PIN_MASK;
spin_unlock_irqrestore(&priv->lock, flags);
/* FIXME: check if this is correct (active high/low) */
if ((pin_state & PIN_RTS) != 0)
result |= TIOCM_RTS;
if ((pin_state & PIN_CTS) != 0)
result |= TIOCM_CTS;
if ((pin_state & PIN_DSR) != 0)
result |= TIOCM_DSR;
if ((pin_state & PIN_DTR) != 0)
result |= TIOCM_DTR;
if ((pin_state & PIN_RI) != 0)
result |= TIOCM_RI;
if ((pin_state & PIN_DCD) != 0)
result |= TIOCM_CD;
dev_dbg(&port->dev, "%s() = 0x%08x\n", __func__, result);
return result;
}
static void oti6858_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct oti6858_private *priv = usb_get_serial_port_data(port);
int transient = 0, can_recv = 0, resubmit = 1;
int status = urb->status;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&urb->dev->dev, "%s(): urb shutting down with status: %d\n",
__func__, status);
return;
default:
dev_dbg(&urb->dev->dev, "%s(): nonzero urb status received: %d\n",
__func__, status);
break;
}
if (status == 0 && urb->actual_length == OTI6858_CTRL_PKT_SIZE) {
struct oti6858_control_pkt *xs = urb->transfer_buffer;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (!priv->transient) {
if (!OTI6858_CTRL_EQUALS_PENDING(xs, priv)) {
if (xs->rx_bytes_avail == 0) {
priv->transient = 4;
priv->setup_done = 0;
resubmit = 0;
dev_dbg(&port->dev, "%s(): scheduling setup_line()\n", __func__);
schedule_delayed_work(&priv->delayed_setup_work, 0);
}
}
} else {
if (OTI6858_CTRL_EQUALS_PENDING(xs, priv)) {
priv->transient = 0;
} else if (!priv->setup_done) {
resubmit = 0;
} else if (--priv->transient == 0) {
if (xs->rx_bytes_avail == 0) {
priv->transient = 4;
priv->setup_done = 0;
resubmit = 0;
dev_dbg(&port->dev, "%s(): scheduling setup_line()\n", __func__);
schedule_delayed_work(&priv->delayed_setup_work, 0);
}
}
}
if (!priv->transient) {
u8 delta = xs->pin_state ^ priv->status.pin_state;
if (delta & PIN_MSR_MASK) {
if (delta & PIN_CTS)
port->icount.cts++;
if (delta & PIN_DSR)
port->icount.dsr++;
if (delta & PIN_RI)
port->icount.rng++;
if (delta & PIN_DCD)
port->icount.dcd++;
wake_up_interruptible(&port->port.delta_msr_wait);
}
memcpy(&priv->status, xs, OTI6858_CTRL_PKT_SIZE);
}
if (!priv->transient && xs->rx_bytes_avail != 0) {
can_recv = xs->rx_bytes_avail;
priv->flags.read_urb_in_use = 1;
}
transient = priv->transient;
spin_unlock_irqrestore(&priv->lock, flags);
}
if (can_recv) {
int result;
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result != 0) {
priv->flags.read_urb_in_use = 0;
dev_err(&port->dev, "%s(): usb_submit_urb() failed,"
" error %d\n", __func__, result);
} else {
resubmit = 0;
}
} else if (!transient) {
unsigned long flags;
int count;
spin_lock_irqsave(&port->lock, flags);
count = kfifo_len(&port->write_fifo);
spin_unlock_irqrestore(&port->lock, flags);
spin_lock_irqsave(&priv->lock, flags);
if (priv->flags.write_urb_in_use == 0 && count != 0) {
schedule_delayed_work(&priv->delayed_write_work, 0);
resubmit = 0;
}
spin_unlock_irqrestore(&priv->lock, flags);
}
if (resubmit) {
int result;
/* dev_dbg(&urb->dev->dev, "%s(): submitting interrupt urb\n", __func__); */
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result != 0) {
dev_err(&urb->dev->dev,
"%s(): usb_submit_urb() failed with"
" error %d\n", __func__, result);
}
}
}
static void oti6858_read_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct oti6858_private *priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
unsigned long flags;
int status = urb->status;
int result;
spin_lock_irqsave(&priv->lock, flags);
priv->flags.read_urb_in_use = 0;
spin_unlock_irqrestore(&priv->lock, flags);
if (status != 0) {
dev_dbg(&urb->dev->dev, "%s(): unable to handle the error, exiting\n", __func__);
return;
}
if (urb->actual_length > 0) {
tty_insert_flip_string(&port->port, data, urb->actual_length);
tty_flip_buffer_push(&port->port);
}
/* schedule the interrupt urb */
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
if (result != 0 && result != -EPERM) {
dev_err(&port->dev, "%s(): usb_submit_urb() failed,"
" error %d\n", __func__, result);
}
}
static void oti6858_write_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct oti6858_private *priv = usb_get_serial_port_data(port);
int status = urb->status;
int result;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&urb->dev->dev, "%s(): urb shutting down with status: %d\n", __func__, status);
priv->flags.write_urb_in_use = 0;
return;
default:
/* error in the urb, so we have to resubmit it */
dev_dbg(&urb->dev->dev, "%s(): nonzero write bulk status received: %d\n", __func__, status);
dev_dbg(&urb->dev->dev, "%s(): overflow in write\n", __func__);
port->write_urb->transfer_buffer_length = 1;
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result) {
dev_err_console(port, "%s(): usb_submit_urb() failed,"
" error %d\n", __func__, result);
} else {
return;
}
}
priv->flags.write_urb_in_use = 0;
/* schedule the interrupt urb if we are still open */
dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
if (result != 0) {
dev_err(&port->dev, "%s(): failed submitting int urb,"
" error %d\n", __func__, result);
}
}
module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(OTI6858_DESCRIPTION);
MODULE_AUTHOR(OTI6858_AUTHOR);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,15 @@
/*
* Ours Technology Inc. OTi-6858 USB to serial adapter driver.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __LINUX_USB_SERIAL_OTI6858_H
#define __LINUX_USB_SERIAL_OTI6858_H
#define OTI6858_VENDOR_ID 0x0ea0
#define OTI6858_PRODUCT_ID 0x6858
#endif

941
drivers/usb/serial/pl2303.c Normal file
View file

@ -0,0 +1,941 @@
/*
* Prolific PL2303 USB to serial adaptor driver
*
* Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2003 IBM Corp.
*
* Original driver for 2.2.x by anonymous
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <asm/unaligned.h>
#include "pl2303.h"
#define PL2303_QUIRK_UART_STATE_IDX0 BIT(0)
#define PL2303_QUIRK_LEGACY BIT(1)
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
{ USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) },
{ USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) },
{ USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) },
{ USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) },
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) },
{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
{ USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) },
{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) },
{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1),
.driver_info = PL2303_QUIRK_UART_STATE_IDX0 },
{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65),
.driver_info = PL2303_QUIRK_UART_STATE_IDX0 },
{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75),
.driver_info = PL2303_QUIRK_UART_STATE_IDX0 },
{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81) },
{ USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_ID_S81) }, /* Benq/Siemens S81 */
{ USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) },
{ USB_DEVICE(NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID) },
{ USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) },
{ USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) },
{ USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) },
{ USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) },
{ USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) },
{ USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) },
{ USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) },
{ USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) },
{ USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) },
{ USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) },
{ USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) },
{ USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) },
{ USB_DEVICE(HP_VENDOR_ID, HP_LD960_PRODUCT_ID) },
{ USB_DEVICE(HP_VENDOR_ID, HP_LCM220_PRODUCT_ID) },
{ USB_DEVICE(HP_VENDOR_ID, HP_LCM960_PRODUCT_ID) },
{ USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) },
{ USB_DEVICE(ZEAGLE_VENDOR_ID, ZEAGLE_N2ITION3_PRODUCT_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) },
{ USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) },
{ USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) },
{ USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
#define SET_LINE_REQUEST_TYPE 0x21
#define SET_LINE_REQUEST 0x20
#define SET_CONTROL_REQUEST_TYPE 0x21
#define SET_CONTROL_REQUEST 0x22
#define CONTROL_DTR 0x01
#define CONTROL_RTS 0x02
#define BREAK_REQUEST_TYPE 0x21
#define BREAK_REQUEST 0x23
#define BREAK_ON 0xffff
#define BREAK_OFF 0x0000
#define GET_LINE_REQUEST_TYPE 0xa1
#define GET_LINE_REQUEST 0x21
#define VENDOR_WRITE_REQUEST_TYPE 0x40
#define VENDOR_WRITE_REQUEST 0x01
#define VENDOR_READ_REQUEST_TYPE 0xc0
#define VENDOR_READ_REQUEST 0x01
#define UART_STATE_INDEX 8
#define UART_STATE_MSR_MASK 0x8b
#define UART_STATE_TRANSIENT_MASK 0x74
#define UART_DCD 0x01
#define UART_DSR 0x02
#define UART_BREAK_ERROR 0x04
#define UART_RING 0x08
#define UART_FRAME_ERROR 0x10
#define UART_PARITY_ERROR 0x20
#define UART_OVERRUN_ERROR 0x40
#define UART_CTS 0x80
enum pl2303_type {
TYPE_01, /* Type 0 and 1 (difference unknown) */
TYPE_HX, /* HX version of the pl2303 chip */
TYPE_COUNT
};
struct pl2303_type_data {
speed_t max_baud_rate;
unsigned long quirks;
};
struct pl2303_serial_private {
const struct pl2303_type_data *type;
unsigned long quirks;
};
struct pl2303_private {
spinlock_t lock;
u8 line_control;
u8 line_status;
u8 line_settings[7];
};
static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
[TYPE_01] = {
.max_baud_rate = 1228800,
.quirks = PL2303_QUIRK_LEGACY,
},
[TYPE_HX] = {
.max_baud_rate = 12000000,
},
};
static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
unsigned char buf[1])
{
struct device *dev = &serial->interface->dev;
int res;
res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
value, 0, buf, 1, 100);
if (res != 1) {
dev_err(dev, "%s - failed to read [%04x]: %d\n", __func__,
value, res);
if (res >= 0)
res = -EIO;
return res;
}
dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, buf[0]);
return 0;
}
static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index)
{
struct device *dev = &serial->interface->dev;
int res;
dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, index);
res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
value, index, NULL, 0, 100);
if (res) {
dev_err(dev, "%s - failed to write [%04x]: %d\n", __func__,
value, res);
return res;
}
return 0;
}
static int pl2303_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
usb_set_serial_data(serial, (void *)id->driver_info);
return 0;
}
static int pl2303_startup(struct usb_serial *serial)
{
struct pl2303_serial_private *spriv;
enum pl2303_type type = TYPE_01;
unsigned char *buf;
spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
if (!spriv)
return -ENOMEM;
buf = kmalloc(1, GFP_KERNEL);
if (!buf) {
kfree(spriv);
return -ENOMEM;
}
if (serial->dev->descriptor.bDeviceClass == 0x02)
type = TYPE_01; /* type 0 */
else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40)
type = TYPE_HX;
else if (serial->dev->descriptor.bDeviceClass == 0x00)
type = TYPE_01; /* type 1 */
else if (serial->dev->descriptor.bDeviceClass == 0xFF)
type = TYPE_01; /* type 1 */
dev_dbg(&serial->interface->dev, "device type: %d\n", type);
spriv->type = &pl2303_type_data[type];
spriv->quirks = (unsigned long)usb_get_serial_data(serial);
spriv->quirks |= spriv->type->quirks;
usb_set_serial_data(serial, spriv);
pl2303_vendor_read(serial, 0x8484, buf);
pl2303_vendor_write(serial, 0x0404, 0);
pl2303_vendor_read(serial, 0x8484, buf);
pl2303_vendor_read(serial, 0x8383, buf);
pl2303_vendor_read(serial, 0x8484, buf);
pl2303_vendor_write(serial, 0x0404, 1);
pl2303_vendor_read(serial, 0x8484, buf);
pl2303_vendor_read(serial, 0x8383, buf);
pl2303_vendor_write(serial, 0, 1);
pl2303_vendor_write(serial, 1, 0);
if (spriv->quirks & PL2303_QUIRK_LEGACY)
pl2303_vendor_write(serial, 2, 0x24);
else
pl2303_vendor_write(serial, 2, 0x44);
kfree(buf);
return 0;
}
static void pl2303_release(struct usb_serial *serial)
{
struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
kfree(spriv);
}
static int pl2303_port_probe(struct usb_serial_port *port)
{
struct pl2303_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
usb_set_serial_port_data(port, priv);
port->port.drain_delay = 256;
return 0;
}
static int pl2303_port_remove(struct usb_serial_port *port)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value)
{
struct usb_device *dev = port->serial->dev;
int retval;
dev_dbg(&port->dev, "%s - %02x\n", __func__, value);
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
value, 0, NULL, 0, 100);
if (retval)
dev_err(&port->dev, "%s - failed: %d\n", __func__, retval);
return retval;
}
/*
* Returns the nearest supported baud rate that can be set directly without
* using divisors.
*/
static speed_t pl2303_get_supported_baud_rate(speed_t baud)
{
static const speed_t baud_sup[] = {
75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600,
14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800,
614400, 921600, 1228800, 2457600, 3000000, 6000000
};
unsigned i;
for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) {
if (baud_sup[i] > baud)
break;
}
if (i == ARRAY_SIZE(baud_sup))
baud = baud_sup[i - 1];
else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1]))
baud = baud_sup[i - 1];
else
baud = baud_sup[i];
return baud;
}
/*
* NOTE: If unsupported baud rates are set directly, the PL2303 seems to
* use 9600 baud.
*/
static speed_t pl2303_encode_baud_rate_direct(unsigned char buf[4],
speed_t baud)
{
put_unaligned_le32(baud, buf);
return baud;
}
static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4],
speed_t baud)
{
unsigned int tmp;
/*
* Apparently the formula is:
* baudrate = 12M * 32 / (2^buf[1]) / buf[0]
*/
tmp = 12000000 * 32 / baud;
buf[3] = 0x80;
buf[2] = 0;
buf[1] = (tmp >= 256);
while (tmp >= 256) {
tmp >>= 2;
buf[1] <<= 1;
}
buf[0] = tmp;
return baud;
}
static void pl2303_encode_baud_rate(struct tty_struct *tty,
struct usb_serial_port *port,
u8 buf[4])
{
struct usb_serial *serial = port->serial;
struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
speed_t baud_sup;
speed_t baud;
baud = tty_get_baud_rate(tty);
dev_dbg(&port->dev, "baud requested = %u\n", baud);
if (!baud)
return;
if (spriv->type->max_baud_rate)
baud = min_t(speed_t, baud, spriv->type->max_baud_rate);
/*
* Use direct method for supported baud rates, otherwise use divisors.
*/
baud_sup = pl2303_get_supported_baud_rate(baud);
if (baud == baud_sup)
baud = pl2303_encode_baud_rate_direct(buf, baud);
else
baud = pl2303_encode_baud_rate_divisor(buf, baud);
/* Save resulting baud rate */
tty_encode_baud_rate(tty, baud, baud);
dev_dbg(&port->dev, "baud set = %u\n", baud);
}
static int pl2303_get_line_request(struct usb_serial_port *port,
unsigned char buf[7])
{
struct usb_device *udev = port->serial->dev;
int ret;
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
0, 0, buf, 7, 100);
if (ret != 7) {
dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
if (ret > 0)
ret = -EIO;
return ret;
}
dev_dbg(&port->dev, "%s - %7ph\n", __func__, buf);
return 0;
}
static int pl2303_set_line_request(struct usb_serial_port *port,
unsigned char buf[7])
{
struct usb_device *udev = port->serial->dev;
int ret;
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
0, 0, buf, 7, 100);
if (ret != 7) {
dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
if (ret > 0)
ret = -EIO;
return ret;
}
dev_dbg(&port->dev, "%s - %7ph\n", __func__, buf);
return 0;
}
static void pl2303_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct usb_serial *serial = port->serial;
struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
unsigned char *buf;
int ret;
u8 control;
if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
return;
buf = kzalloc(7, GFP_KERNEL);
if (!buf) {
/* Report back no change occurred */
if (old_termios)
tty->termios = *old_termios;
return;
}
pl2303_get_line_request(port, buf);
switch (C_CSIZE(tty)) {
case CS5:
buf[6] = 5;
break;
case CS6:
buf[6] = 6;
break;
case CS7:
buf[6] = 7;
break;
default:
case CS8:
buf[6] = 8;
}
dev_dbg(&port->dev, "data bits = %d\n", buf[6]);
/* For reference buf[0]:buf[3] baud rate value */
pl2303_encode_baud_rate(tty, port, &buf[0]);
/* For reference buf[4]=0 is 1 stop bits */
/* For reference buf[4]=1 is 1.5 stop bits */
/* For reference buf[4]=2 is 2 stop bits */
if (C_CSTOPB(tty)) {
/*
* NOTE: Comply with "real" UARTs / RS232:
* use 1.5 instead of 2 stop bits with 5 data bits
*/
if (C_CSIZE(tty) == CS5) {
buf[4] = 1;
dev_dbg(&port->dev, "stop bits = 1.5\n");
} else {
buf[4] = 2;
dev_dbg(&port->dev, "stop bits = 2\n");
}
} else {
buf[4] = 0;
dev_dbg(&port->dev, "stop bits = 1\n");
}
if (C_PARENB(tty)) {
/* For reference buf[5]=0 is none parity */
/* For reference buf[5]=1 is odd parity */
/* For reference buf[5]=2 is even parity */
/* For reference buf[5]=3 is mark parity */
/* For reference buf[5]=4 is space parity */
if (C_PARODD(tty)) {
if (C_CMSPAR(tty)) {
buf[5] = 3;
dev_dbg(&port->dev, "parity = mark\n");
} else {
buf[5] = 1;
dev_dbg(&port->dev, "parity = odd\n");
}
} else {
if (C_CMSPAR(tty)) {
buf[5] = 4;
dev_dbg(&port->dev, "parity = space\n");
} else {
buf[5] = 2;
dev_dbg(&port->dev, "parity = even\n");
}
}
} else {
buf[5] = 0;
dev_dbg(&port->dev, "parity = none\n");
}
/*
* Some PL2303 are known to lose bytes if you change serial settings
* even to the same values as before. Thus we actually need to filter
* in this specific case.
*
* Note that the tty_termios_hw_change check above is not sufficient
* as a previously requested baud rate may differ from the one
* actually used (and stored in old_termios).
*
* NOTE: No additional locking needed for line_settings as it is
* only used in set_termios, which is serialised against itself.
*/
if (!old_termios || memcmp(buf, priv->line_settings, 7)) {
ret = pl2303_set_line_request(port, buf);
if (!ret)
memcpy(priv->line_settings, buf, 7);
}
/* change control lines if we are switching to or from B0 */
spin_lock_irqsave(&priv->lock, flags);
control = priv->line_control;
if (C_BAUD(tty) == B0)
priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
if (control != priv->line_control) {
control = priv->line_control;
spin_unlock_irqrestore(&priv->lock, flags);
pl2303_set_control_lines(port, control);
} else {
spin_unlock_irqrestore(&priv->lock, flags);
}
if (C_CRTSCTS(tty)) {
if (spriv->quirks & PL2303_QUIRK_LEGACY)
pl2303_vendor_write(serial, 0x0, 0x41);
else
pl2303_vendor_write(serial, 0x0, 0x61);
} else {
pl2303_vendor_write(serial, 0x0, 0x0);
}
kfree(buf);
}
static void pl2303_dtr_rts(struct usb_serial_port *port, int on)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
u8 control;
spin_lock_irqsave(&priv->lock, flags);
if (on)
priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
else
priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
control = priv->line_control;
spin_unlock_irqrestore(&priv->lock, flags);
pl2303_set_control_lines(port, control);
}
static void pl2303_close(struct usb_serial_port *port)
{
usb_serial_generic_close(port);
usb_kill_urb(port->interrupt_in_urb);
}
static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
int result;
if (spriv->quirks & PL2303_QUIRK_LEGACY) {
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
} else {
/* reset upstream data pipes */
pl2303_vendor_write(serial, 8, 0);
pl2303_vendor_write(serial, 9, 0);
}
/* Setup termios */
if (tty)
pl2303_set_termios(tty, port, NULL);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result) {
dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
result);
return result;
}
result = usb_serial_generic_open(tty, port);
if (result) {
usb_kill_urb(port->interrupt_in_urb);
return result;
}
return 0;
}
static int pl2303_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
u8 control;
int ret;
spin_lock_irqsave(&priv->lock, flags);
if (set & TIOCM_RTS)
priv->line_control |= CONTROL_RTS;
if (set & TIOCM_DTR)
priv->line_control |= CONTROL_DTR;
if (clear & TIOCM_RTS)
priv->line_control &= ~CONTROL_RTS;
if (clear & TIOCM_DTR)
priv->line_control &= ~CONTROL_DTR;
control = priv->line_control;
spin_unlock_irqrestore(&priv->lock, flags);
ret = pl2303_set_control_lines(port, control);
if (ret)
return usb_translate_errors(ret);
return 0;
}
static int pl2303_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
unsigned int mcr;
unsigned int status;
unsigned int result;
spin_lock_irqsave(&priv->lock, flags);
mcr = priv->line_control;
status = priv->line_status;
spin_unlock_irqrestore(&priv->lock, flags);
result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0)
| ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0)
| ((status & UART_CTS) ? TIOCM_CTS : 0)
| ((status & UART_DSR) ? TIOCM_DSR : 0)
| ((status & UART_RING) ? TIOCM_RI : 0)
| ((status & UART_DCD) ? TIOCM_CD : 0);
dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
return result;
}
static int pl2303_carrier_raised(struct usb_serial_port *port)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
if (priv->line_status & UART_DCD)
return 1;
return 0;
}
static int pl2303_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct serial_struct ser;
struct usb_serial_port *port = tty->driver_data;
switch (cmd) {
case TIOCGSERIAL:
memset(&ser, 0, sizeof ser);
ser.type = PORT_16654;
ser.line = port->minor;
ser.port = port->port_number;
ser.baud_base = 460800;
if (copy_to_user((void __user *)arg, &ser, sizeof ser))
return -EFAULT;
return 0;
default:
break;
}
return -ENOIOCTLCMD;
}
static void pl2303_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
u16 state;
int result;
if (break_state == 0)
state = BREAK_OFF;
else
state = BREAK_ON;
dev_dbg(&port->dev, "%s - turning break %s\n", __func__,
state == BREAK_OFF ? "off" : "on");
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
0, NULL, 0, 100);
if (result)
dev_err(&port->dev, "error sending break = %d\n", result);
}
static void pl2303_update_line_status(struct usb_serial_port *port,
unsigned char *data,
unsigned int actual_length)
{
struct usb_serial *serial = port->serial;
struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
struct pl2303_private *priv = usb_get_serial_port_data(port);
struct tty_struct *tty;
unsigned long flags;
unsigned int status_idx = UART_STATE_INDEX;
u8 status;
u8 delta;
if (spriv->quirks & PL2303_QUIRK_UART_STATE_IDX0)
status_idx = 0;
if (actual_length < status_idx + 1)
return;
status = data[status_idx];
/* Save off the uart status for others to look at */
spin_lock_irqsave(&priv->lock, flags);
delta = priv->line_status ^ status;
priv->line_status = status;
spin_unlock_irqrestore(&priv->lock, flags);
if (status & UART_BREAK_ERROR)
usb_serial_handle_break(port);
if (delta & UART_STATE_MSR_MASK) {
if (delta & UART_CTS)
port->icount.cts++;
if (delta & UART_DSR)
port->icount.dsr++;
if (delta & UART_RING)
port->icount.rng++;
if (delta & UART_DCD) {
port->icount.dcd++;
tty = tty_port_tty_get(&port->port);
if (tty) {
usb_serial_handle_dcd_change(port, tty,
status & UART_DCD);
tty_kref_put(tty);
}
}
wake_up_interruptible(&port->port.delta_msr_wait);
}
}
static void pl2303_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
unsigned int actual_length = urb->actual_length;
int status = urb->status;
int retval;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
__func__, status);
goto exit;
}
usb_serial_debug_data(&port->dev, __func__,
urb->actual_length, urb->transfer_buffer);
pl2303_update_line_status(port, data, actual_length);
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval) {
dev_err(&port->dev,
"%s - usb_submit_urb failed with result %d\n",
__func__, retval);
}
}
static void pl2303_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
char tty_flag = TTY_NORMAL;
unsigned long flags;
u8 line_status;
int i;
/* update line status */
spin_lock_irqsave(&priv->lock, flags);
line_status = priv->line_status;
priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
spin_unlock_irqrestore(&priv->lock, flags);
if (!urb->actual_length)
return;
/*
* Break takes precedence over parity, which takes precedence over
* framing errors.
*/
if (line_status & UART_BREAK_ERROR)
tty_flag = TTY_BREAK;
else if (line_status & UART_PARITY_ERROR)
tty_flag = TTY_PARITY;
else if (line_status & UART_FRAME_ERROR)
tty_flag = TTY_FRAME;
if (tty_flag != TTY_NORMAL)
dev_dbg(&port->dev, "%s - tty_flag = %d\n", __func__,
tty_flag);
/* overrun is special, not associated with a char */
if (line_status & UART_OVERRUN_ERROR)
tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
if (port->port.console && port->sysrq) {
for (i = 0; i < urb->actual_length; ++i)
if (!usb_serial_handle_sysrq_char(port, data[i]))
tty_insert_flip_char(&port->port, data[i],
tty_flag);
} else {
tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag,
urb->actual_length);
}
tty_flip_buffer_push(&port->port);
}
static struct usb_serial_driver pl2303_device = {
.driver = {
.owner = THIS_MODULE,
.name = "pl2303",
},
.id_table = id_table,
.num_ports = 1,
.bulk_in_size = 256,
.bulk_out_size = 256,
.open = pl2303_open,
.close = pl2303_close,
.dtr_rts = pl2303_dtr_rts,
.carrier_raised = pl2303_carrier_raised,
.ioctl = pl2303_ioctl,
.break_ctl = pl2303_break_ctl,
.set_termios = pl2303_set_termios,
.tiocmget = pl2303_tiocmget,
.tiocmset = pl2303_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.process_read_urb = pl2303_process_read_urb,
.read_int_callback = pl2303_read_int_callback,
.probe = pl2303_probe,
.attach = pl2303_startup,
.release = pl2303_release,
.port_probe = pl2303_port_probe,
.port_remove = pl2303_port_remove,
};
static struct usb_serial_driver * const serial_drivers[] = {
&pl2303_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION("Prolific PL2303 USB to serial adaptor driver");
MODULE_LICENSE("GPL");

155
drivers/usb/serial/pl2303.h Normal file
View file

@ -0,0 +1,155 @@
/*
* Prolific PL2303 USB to serial adaptor driver header file
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/
#define BENQ_VENDOR_ID 0x04a5
#define BENQ_PRODUCT_ID_S81 0x4027
#define PL2303_VENDOR_ID 0x067b
#define PL2303_PRODUCT_ID 0x2303
#define PL2303_PRODUCT_ID_RSAQ2 0x04bb
#define PL2303_PRODUCT_ID_DCU11 0x1234
#define PL2303_PRODUCT_ID_PHAROS 0xaaa0
#define PL2303_PRODUCT_ID_RSAQ3 0xaaa2
#define PL2303_PRODUCT_ID_ALDIGA 0x0611
#define PL2303_PRODUCT_ID_MMX 0x0612
#define PL2303_PRODUCT_ID_GPRS 0x0609
#define PL2303_PRODUCT_ID_HCR331 0x331a
#define PL2303_PRODUCT_ID_MOTOROLA 0x0307
#define PL2303_PRODUCT_ID_ZTEK 0xe1f1
#define ATEN_VENDOR_ID 0x0557
#define ATEN_VENDOR_ID2 0x0547
#define ATEN_PRODUCT_ID 0x2008
#define IODATA_VENDOR_ID 0x04bb
#define IODATA_PRODUCT_ID 0x0a03
#define IODATA_PRODUCT_ID_RSAQ5 0x0a0e
#define ELCOM_VENDOR_ID 0x056e
#define ELCOM_PRODUCT_ID 0x5003
#define ELCOM_PRODUCT_ID_UCSGT 0x5004
#define ITEGNO_VENDOR_ID 0x0eba
#define ITEGNO_PRODUCT_ID 0x1080
#define ITEGNO_PRODUCT_ID_2080 0x2080
#define MA620_VENDOR_ID 0x0df7
#define MA620_PRODUCT_ID 0x0620
#define RATOC_VENDOR_ID 0x0584
#define RATOC_PRODUCT_ID 0xb000
#define TRIPP_VENDOR_ID 0x2478
#define TRIPP_PRODUCT_ID 0x2008
#define RADIOSHACK_VENDOR_ID 0x1453
#define RADIOSHACK_PRODUCT_ID 0x4026
#define DCU10_VENDOR_ID 0x0731
#define DCU10_PRODUCT_ID 0x0528
#define SITECOM_VENDOR_ID 0x6189
#define SITECOM_PRODUCT_ID 0x2068
/* Alcatel OT535/735 USB cable */
#define ALCATEL_VENDOR_ID 0x11f7
#define ALCATEL_PRODUCT_ID 0x02df
/* Samsung I330 phone cradle */
#define SAMSUNG_VENDOR_ID 0x04e8
#define SAMSUNG_PRODUCT_ID 0x8001
#define SIEMENS_VENDOR_ID 0x11f5
#define SIEMENS_PRODUCT_ID_SX1 0x0001
#define SIEMENS_PRODUCT_ID_X65 0x0003
#define SIEMENS_PRODUCT_ID_X75 0x0004
#define SIEMENS_PRODUCT_ID_EF81 0x0005
#define SYNTECH_VENDOR_ID 0x0745
#define SYNTECH_PRODUCT_ID 0x0001
/* Nokia CA-42 Cable */
#define NOKIA_CA42_VENDOR_ID 0x078b
#define NOKIA_CA42_PRODUCT_ID 0x1234
/* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */
#define CA_42_CA42_VENDOR_ID 0x10b5
#define CA_42_CA42_PRODUCT_ID 0xac70
#define SAGEM_VENDOR_ID 0x079b
#define SAGEM_PRODUCT_ID 0x0027
/* Leadtek GPS 9531 (ID 0413:2101) */
#define LEADTEK_VENDOR_ID 0x0413
#define LEADTEK_9531_PRODUCT_ID 0x2101
/* USB GSM cable from Speed Dragon Multimedia, Ltd */
#define SPEEDDRAGON_VENDOR_ID 0x0e55
#define SPEEDDRAGON_PRODUCT_ID 0x110b
/* DATAPILOT Universal-2 Phone Cable */
#define DATAPILOT_U2_VENDOR_ID 0x0731
#define DATAPILOT_U2_PRODUCT_ID 0x2003
/* Belkin "F5U257" Serial Adapter */
#define BELKIN_VENDOR_ID 0x050d
#define BELKIN_PRODUCT_ID 0x0257
/* Alcor Micro Corp. USB 2.0 TO RS-232 */
#define ALCOR_VENDOR_ID 0x058F
#define ALCOR_PRODUCT_ID 0x9720
/* Willcom WS002IN Data Driver (by NetIndex Inc.) */
#define WS002IN_VENDOR_ID 0x11f6
#define WS002IN_PRODUCT_ID 0x2001
/* Corega CG-USBRS232R Serial Adapter */
#define COREGA_VENDOR_ID 0x07aa
#define COREGA_PRODUCT_ID 0x002a
/* Y.C. Cable U.S.A., Inc - USB to RS-232 */
#define YCCABLE_VENDOR_ID 0x05ad
#define YCCABLE_PRODUCT_ID 0x0fba
/* "Superial" USB - Serial */
#define SUPERIAL_VENDOR_ID 0x5372
#define SUPERIAL_PRODUCT_ID 0x2303
/* Hewlett-Packard POS Pole Displays */
#define HP_VENDOR_ID 0x03f0
#define HP_LD960_PRODUCT_ID 0x0b39
#define HP_LCM220_PRODUCT_ID 0x3139
#define HP_LCM960_PRODUCT_ID 0x3239
#define HP_LD220_PRODUCT_ID 0x3524
/* Cressi Edy (diving computer) PC interface */
#define CRESSI_VENDOR_ID 0x04b8
#define CRESSI_EDY_PRODUCT_ID 0x0521
/* Zeagle dive computer interface */
#define ZEAGLE_VENDOR_ID 0x04b8
#define ZEAGLE_N2ITION3_PRODUCT_ID 0x0522
/* Sony, USB data cable for CMD-Jxx mobile phones */
#define SONY_VENDOR_ID 0x054c
#define SONY_QN3USB_PRODUCT_ID 0x0437
/* Sanwa KB-USB2 multimeter cable (ID: 11ad:0001) */
#define SANWA_VENDOR_ID 0x11ad
#define SANWA_PRODUCT_ID 0x0001
/* ADLINK ND-6530 RS232,RS485 and RS422 adapter */
#define ADLINK_VENDOR_ID 0x0b63
#define ADLINK_ND6530_PRODUCT_ID 0x6530
/* SMART USB Serial Adapter */
#define SMART_VENDOR_ID 0x0b8c
#define SMART_PRODUCT_ID 0x2303

View file

@ -0,0 +1,90 @@
/*
* Qualcomm USB Auxiliary Serial Port driver
*
* Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2010 Dan Williams <dcbw@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Devices listed here usually provide a CDC ACM port on which normal modem
* AT commands and PPP can be used. But when that port is in-use by PPP it
* cannot be used simultaneously for status or signal strength. Instead, the
* ports here can be queried for that information using the Qualcomm DM
* protocol.
*/
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
/* NOTE: for now, only use this driver for devices that provide a CDC-ACM port
* for normal AT commands, but also provide secondary USB interfaces for the
* QCDM-capable ports. Devices that do not provide a CDC-ACM port should
* probably be driven by option.ko.
*/
/* UTStarcom/Pantech/Curitel devices */
#define UTSTARCOM_VENDOR_ID 0x106c
#define UTSTARCOM_PRODUCT_PC5740 0x3701
#define UTSTARCOM_PRODUCT_PC5750 0x3702 /* aka Pantech PX-500 */
#define UTSTARCOM_PRODUCT_UM150 0x3711
#define UTSTARCOM_PRODUCT_UM175_V1 0x3712
#define UTSTARCOM_PRODUCT_UM175_V2 0x3714
#define UTSTARCOM_PRODUCT_UM175_ALLTEL 0x3715
/* CMOTECH devices */
#define CMOTECH_VENDOR_ID 0x16d8
#define CMOTECH_PRODUCT_CDU550 0x5553
#define CMOTECH_PRODUCT_CDX650 0x6512
/* LG devices */
#define LG_VENDOR_ID 0x1004
#define LG_PRODUCT_VX4400_6000 0x6000 /* VX4400/VX6000/Rumor */
/* Sanyo devices */
#define SANYO_VENDOR_ID 0x0474
#define SANYO_PRODUCT_KATANA_LX 0x0754 /* SCP-3800 (Katana LX) */
/* Samsung devices */
#define SAMSUNG_VENDOR_ID 0x04e8
#define SAMSUNG_PRODUCT_U520 0x6640 /* SCH-U520 */
static const struct usb_device_id id_table[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_PC5740, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_PC5750, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_UM150, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_UM175_V1, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_UM175_V2, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_UM175_ALLTEL, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDU550, 0xff, 0xff, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDX650, 0xff, 0xff, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(LG_VENDOR_ID, LG_PRODUCT_VX4400_6000, 0xff, 0xff, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(SANYO_VENDOR_ID, SANYO_PRODUCT_KATANA_LX, 0xff, 0xff, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_U520, 0xff, 0x00, 0x00) },
{ USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xfd, 0xff) }, /* NMEA */
{ USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xfe, 0xff) }, /* WMC */
{ USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xff, 0xff) }, /* DIAG */
{ USB_DEVICE_AND_INTERFACE_INFO(0x1fac, 0x0151, 0xff, 0xff, 0xff) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver qcaux_device = {
.driver = {
.owner = THIS_MODULE,
.name = "qcaux",
},
.id_table = id_table,
.num_ports = 1,
};
static struct usb_serial_driver * const serial_drivers[] = {
&qcaux_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,397 @@
/*
* Qualcomm Serial USB driver
*
* Copyright (c) 2008 QUALCOMM Incorporated.
* Copyright (c) 2009 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (c) 2009 Novell Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
*/
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/slab.h>
#include "usb-wwan.h"
#define DRIVER_AUTHOR "Qualcomm Inc"
#define DRIVER_DESC "Qualcomm USB Serial driver"
/* standard device layouts supported by this driver */
enum qcserial_layouts {
QCSERIAL_G2K = 0, /* Gobi 2000 */
QCSERIAL_G1K = 1, /* Gobi 1000 */
QCSERIAL_SWI = 2, /* Sierra Wireless */
QCSERIAL_HWI = 3, /* Huawei */
};
#define DEVICE_G1K(v, p) \
USB_DEVICE(v, p), .driver_info = QCSERIAL_G1K
#define DEVICE_SWI(v, p) \
USB_DEVICE(v, p), .driver_info = QCSERIAL_SWI
#define DEVICE_HWI(v, p) \
USB_DEVICE(v, p), .driver_info = QCSERIAL_HWI
static const struct usb_device_id id_table[] = {
/* Gobi 1000 devices */
{DEVICE_G1K(0x05c6, 0x9211)}, /* Acer Gobi QDL device */
{DEVICE_G1K(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
{DEVICE_G1K(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */
{DEVICE_G1K(0x03f0, 0x201d)}, /* HP un2400 Gobi QDL Device */
{DEVICE_G1K(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */
{DEVICE_G1K(0x04da, 0x250c)}, /* Panasonic Gobi QDL device */
{DEVICE_G1K(0x413c, 0x8172)}, /* Dell Gobi Modem device */
{DEVICE_G1K(0x413c, 0x8171)}, /* Dell Gobi QDL device */
{DEVICE_G1K(0x1410, 0xa001)}, /* Novatel/Verizon USB-1000 */
{DEVICE_G1K(0x1410, 0xa002)}, /* Novatel Gobi Modem device */
{DEVICE_G1K(0x1410, 0xa003)}, /* Novatel Gobi Modem device */
{DEVICE_G1K(0x1410, 0xa004)}, /* Novatel Gobi Modem device */
{DEVICE_G1K(0x1410, 0xa005)}, /* Novatel Gobi Modem device */
{DEVICE_G1K(0x1410, 0xa006)}, /* Novatel Gobi Modem device */
{DEVICE_G1K(0x1410, 0xa007)}, /* Novatel Gobi Modem device */
{DEVICE_G1K(0x1410, 0xa008)}, /* Novatel Gobi QDL device */
{DEVICE_G1K(0x0b05, 0x1776)}, /* Asus Gobi Modem device */
{DEVICE_G1K(0x0b05, 0x1774)}, /* Asus Gobi QDL device */
{DEVICE_G1K(0x19d2, 0xfff3)}, /* ONDA Gobi Modem device */
{DEVICE_G1K(0x19d2, 0xfff2)}, /* ONDA Gobi QDL device */
{DEVICE_G1K(0x1557, 0x0a80)}, /* OQO Gobi QDL device */
{DEVICE_G1K(0x05c6, 0x9001)}, /* Generic Gobi Modem device */
{DEVICE_G1K(0x05c6, 0x9002)}, /* Generic Gobi Modem device */
{DEVICE_G1K(0x05c6, 0x9202)}, /* Generic Gobi Modem device */
{DEVICE_G1K(0x05c6, 0x9203)}, /* Generic Gobi Modem device */
{DEVICE_G1K(0x05c6, 0x9222)}, /* Generic Gobi Modem device */
{DEVICE_G1K(0x05c6, 0x9008)}, /* Generic Gobi QDL device */
{DEVICE_G1K(0x05c6, 0x9009)}, /* Generic Gobi Modem device */
{DEVICE_G1K(0x05c6, 0x9201)}, /* Generic Gobi QDL device */
{DEVICE_G1K(0x05c6, 0x9221)}, /* Generic Gobi QDL device */
{DEVICE_G1K(0x05c6, 0x9231)}, /* Generic Gobi QDL device */
{DEVICE_G1K(0x1f45, 0x0001)}, /* Unknown Gobi QDL device */
{DEVICE_G1K(0x1bc7, 0x900e)}, /* Telit Gobi QDL device */
/* Gobi 2000 devices */
{USB_DEVICE(0x1410, 0xa010)}, /* Novatel Gobi 2000 QDL device */
{USB_DEVICE(0x1410, 0xa011)}, /* Novatel Gobi 2000 QDL device */
{USB_DEVICE(0x1410, 0xa012)}, /* Novatel Gobi 2000 QDL device */
{USB_DEVICE(0x1410, 0xa013)}, /* Novatel Gobi 2000 QDL device */
{USB_DEVICE(0x1410, 0xa014)}, /* Novatel Gobi 2000 QDL device */
{USB_DEVICE(0x413c, 0x8185)}, /* Dell Gobi 2000 QDL device (N0218, VU936) */
{USB_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */
{USB_DEVICE(0x05c6, 0x9208)}, /* Generic Gobi 2000 QDL device */
{USB_DEVICE(0x05c6, 0x920b)}, /* Generic Gobi 2000 Modem device */
{USB_DEVICE(0x05c6, 0x9224)}, /* Sony Gobi 2000 QDL device (N0279, VU730) */
{USB_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */
{USB_DEVICE(0x05c6, 0x9244)}, /* Samsung Gobi 2000 QDL device (VL176) */
{USB_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */
{USB_DEVICE(0x03f0, 0x241d)}, /* HP Gobi 2000 QDL device (VP412) */
{USB_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */
{USB_DEVICE(0x05c6, 0x9214)}, /* Acer Gobi 2000 QDL device (VP413) */
{USB_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */
{USB_DEVICE(0x05c6, 0x9264)}, /* Asus Gobi 2000 QDL device (VR305) */
{USB_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */
{USB_DEVICE(0x05c6, 0x9234)}, /* Top Global Gobi 2000 QDL device (VR306) */
{USB_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */
{USB_DEVICE(0x05c6, 0x9274)}, /* iRex Technologies Gobi 2000 QDL device (VR307) */
{USB_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */
{USB_DEVICE(0x1199, 0x9000)}, /* Sierra Wireless Gobi 2000 QDL device (VT773) */
{USB_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x9002)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x9003)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x9004)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x9005)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x9006)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x9007)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x9008)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
{USB_DEVICE(0x16d8, 0x8001)}, /* CMDTech Gobi 2000 QDL device (VU922) */
{USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */
{USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */
{USB_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */
/* Gobi 3000 devices */
{USB_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Gobi 3000 QDL */
{USB_DEVICE(0x05c6, 0x920c)}, /* Gobi 3000 QDL */
{USB_DEVICE(0x05c6, 0x920d)}, /* Gobi 3000 Composite */
{USB_DEVICE(0x1410, 0xa020)}, /* Novatel Gobi 3000 QDL */
{USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */
{USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */
{USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */
{USB_DEVICE(0x1199, 0x68a4)}, /* Sierra Wireless QDL */
{USB_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */
{USB_DEVICE(0x1199, 0x68a8)}, /* Sierra Wireless QDL */
{USB_DEVICE(0x1199, 0x68a9)}, /* Sierra Wireless Modem */
{USB_DEVICE(0x1199, 0x9010)}, /* Sierra Wireless Gobi 3000 QDL */
{USB_DEVICE(0x1199, 0x9012)}, /* Sierra Wireless Gobi 3000 QDL */
{USB_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
{USB_DEVICE(0x1199, 0x9014)}, /* Sierra Wireless Gobi 3000 QDL */
{USB_DEVICE(0x1199, 0x9015)}, /* Sierra Wireless Gobi 3000 Modem device */
{USB_DEVICE(0x1199, 0x9018)}, /* Sierra Wireless Gobi 3000 QDL */
{USB_DEVICE(0x1199, 0x9019)}, /* Sierra Wireless Gobi 3000 Modem device */
{USB_DEVICE(0x1199, 0x901b)}, /* Sierra Wireless MC7770 */
{USB_DEVICE(0x12D1, 0x14F0)}, /* Sony Gobi 3000 QDL */
{USB_DEVICE(0x12D1, 0x14F1)}, /* Sony Gobi 3000 Composite */
{USB_DEVICE(0x0AF0, 0x8120)}, /* Option GTM681W */
/* non-Gobi Sierra Wireless devices */
{DEVICE_SWI(0x0f3d, 0x68a2)}, /* Sierra Wireless MC7700 */
{DEVICE_SWI(0x114f, 0x68a2)}, /* Sierra Wireless MC7750 */
{DEVICE_SWI(0x1199, 0x68a2)}, /* Sierra Wireless MC7710 */
{DEVICE_SWI(0x1199, 0x901c)}, /* Sierra Wireless EM7700 */
{DEVICE_SWI(0x1199, 0x901f)}, /* Sierra Wireless EM7355 */
{DEVICE_SWI(0x1199, 0x9040)}, /* Sierra Wireless Modem */
{DEVICE_SWI(0x1199, 0x9041)}, /* Sierra Wireless MC7305/MC7355 */
{DEVICE_SWI(0x1199, 0x9051)}, /* Netgear AirCard 340U */
{DEVICE_SWI(0x1199, 0x9053)}, /* Sierra Wireless Modem */
{DEVICE_SWI(0x1199, 0x9054)}, /* Sierra Wireless Modem */
{DEVICE_SWI(0x1199, 0x9055)}, /* Netgear AirCard 341U */
{DEVICE_SWI(0x1199, 0x9056)}, /* Sierra Wireless Modem */
{DEVICE_SWI(0x1199, 0x9060)}, /* Sierra Wireless Modem */
{DEVICE_SWI(0x1199, 0x9061)}, /* Sierra Wireless Modem */
{DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a9)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
/* Huawei devices */
{DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
{
struct usb_host_interface *intf = serial->interface->cur_altsetting;
struct device *dev = &serial->dev->dev;
int retval = -ENODEV;
__u8 nintf;
__u8 ifnum;
int altsetting = -1;
nintf = serial->dev->actconfig->desc.bNumInterfaces;
dev_dbg(dev, "Num Interfaces = %d\n", nintf);
ifnum = intf->desc.bInterfaceNumber;
dev_dbg(dev, "This Interface = %d\n", ifnum);
if (nintf == 1) {
/* QDL mode */
/* Gobi 2000 has a single altsetting, older ones have two */
if (serial->interface->num_altsetting == 2)
intf = &serial->interface->altsetting[1];
else if (serial->interface->num_altsetting > 2)
goto done;
if (intf->desc.bNumEndpoints == 2 &&
usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
dev_dbg(dev, "QDL port found\n");
if (serial->interface->num_altsetting == 1)
retval = 0; /* Success */
else
altsetting = 1;
}
goto done;
}
/* default to enabling interface */
altsetting = 0;
/*
* Composite mode; don't bind to the QMI/net interface as that
* gets handled by other drivers.
*/
switch (id->driver_info) {
case QCSERIAL_G1K:
/*
* Gobi 1K USB layout:
* 0: DM/DIAG (use libqcdm from ModemManager for communication)
* 1: serial port (doesn't respond)
* 2: AT-capable modem port
* 3: QMI/net
*/
if (nintf < 3 || nintf > 4) {
dev_err(dev, "unknown number of interfaces: %d\n", nintf);
altsetting = -1;
goto done;
}
if (ifnum == 0) {
dev_dbg(dev, "Gobi 1K DM/DIAG interface found\n");
altsetting = 1;
} else if (ifnum == 2)
dev_dbg(dev, "Modem port found\n");
else
altsetting = -1;
break;
case QCSERIAL_G2K:
/*
* Gobi 2K+ USB layout:
* 0: QMI/net
* 1: DM/DIAG (use libqcdm from ModemManager for communication)
* 2: AT-capable modem port
* 3: NMEA
*/
if (nintf < 3 || nintf > 4) {
dev_err(dev, "unknown number of interfaces: %d\n", nintf);
altsetting = -1;
goto done;
}
switch (ifnum) {
case 0:
/* Don't claim the QMI/net interface */
altsetting = -1;
break;
case 1:
dev_dbg(dev, "Gobi 2K+ DM/DIAG interface found\n");
break;
case 2:
dev_dbg(dev, "Modem port found\n");
break;
case 3:
/*
* NMEA (serial line 9600 8N1)
* # echo "\$GPS_START" > /dev/ttyUSBx
* # echo "\$GPS_STOP" > /dev/ttyUSBx
*/
dev_dbg(dev, "Gobi 2K+ NMEA GPS interface found\n");
break;
}
break;
case QCSERIAL_SWI:
/*
* Sierra Wireless layout:
* 0: DM/DIAG (use libqcdm from ModemManager for communication)
* 2: NMEA
* 3: AT-capable modem port
* 8: QMI/net
*/
switch (ifnum) {
case 0:
dev_dbg(dev, "DM/DIAG interface found\n");
break;
case 2:
dev_dbg(dev, "NMEA GPS interface found\n");
break;
case 3:
dev_dbg(dev, "Modem port found\n");
break;
default:
/* don't claim any unsupported interface */
altsetting = -1;
break;
}
break;
case QCSERIAL_HWI:
/*
* Huawei layout:
* 0: AT-capable modem port
* 1: DM/DIAG
* 2: AT-capable modem port
* 3: CCID-compatible PCSC interface
* 4: QMI/net
* 5: NMEA
*/
switch (ifnum) {
case 0:
case 2:
dev_dbg(dev, "Modem port found\n");
break;
case 1:
dev_dbg(dev, "DM/DIAG interface found\n");
break;
case 5:
dev_dbg(dev, "NMEA GPS interface found\n");
break;
default:
/* don't claim any unsupported interface */
altsetting = -1;
break;
}
break;
default:
dev_err(dev, "unsupported device layout type: %lu\n",
id->driver_info);
break;
}
done:
if (altsetting >= 0) {
retval = usb_set_interface(serial->dev, ifnum, altsetting);
if (retval < 0) {
dev_err(dev,
"Could not set interface, error %d\n",
retval);
retval = -ENODEV;
}
}
return retval;
}
static int qc_attach(struct usb_serial *serial)
{
struct usb_wwan_intf_private *data;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
spin_lock_init(&data->susp_lock);
usb_set_serial_data(serial, data);
return 0;
}
static void qc_release(struct usb_serial *serial)
{
struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
usb_set_serial_data(serial, NULL);
kfree(priv);
}
static struct usb_serial_driver qcdevice = {
.driver = {
.owner = THIS_MODULE,
.name = "qcserial",
},
.description = "Qualcomm USB modem",
.id_table = id_table,
.num_ports = 1,
.probe = qcprobe,
.open = usb_wwan_open,
.close = usb_wwan_close,
.write = usb_wwan_write,
.write_room = usb_wwan_write_room,
.chars_in_buffer = usb_wwan_chars_in_buffer,
.attach = qc_attach,
.release = qc_release,
.port_probe = usb_wwan_port_probe,
.port_remove = usb_wwan_port_remove,
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
.resume = usb_wwan_resume,
#endif
};
static struct usb_serial_driver * const serial_drivers[] = {
&qcdevice, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,305 @@
/*
* Safe Encapsulated USB Serial Driver
*
* Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
* Copyright (C) 2001 Lineo
* Copyright (C) 2001 Hewlett-Packard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* By:
* Stuart Lynne <sl@lineo.com>, Tom Rushworth <tbr@lineo.com>
*/
/*
* The encapsultaion is designed to overcome difficulties with some USB
* hardware.
*
* While the USB protocol has a CRC over the data while in transit, i.e. while
* being carried over the bus, there is no end to end protection. If the
* hardware has any problems getting the data into or out of the USB transmit
* and receive FIFO's then data can be lost.
*
* This protocol adds a two byte trailer to each USB packet to specify the
* number of bytes of valid data and a 10 bit CRC that will allow the receiver
* to verify that the entire USB packet was received without error.
*
* Because in this case the sender and receiver are the class and function
* drivers there is now end to end protection.
*
* There is an additional option that can be used to force all transmitted
* packets to be padded to the maximum packet size. This provides a work
* around for some devices which have problems with small USB packets.
*
* Assuming a packetsize of N:
*
* 0..N-2 data and optional padding
*
* N-2 bits 7-2 - number of bytes of valid data
* bits 1-0 top two bits of 10 bit CRC
* N-1 bottom 8 bits of 10 bit CRC
*
*
* | Data Length | 10 bit CRC |
* + 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 | 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 +
*
* The 10 bit CRC is computed across the sent data, followed by the trailer
* with the length set and the CRC set to zero. The CRC is then OR'd into
* the trailer.
*
* When received a 10 bit CRC is computed over the entire frame including
* the trailer and should be equal to zero.
*
* Two module parameters are used to control the encapsulation, if both are
* turned of the module works as a simple serial device with NO
* encapsulation.
*
* See linux/drivers/usbd/serial_fd for a device function driver
* implementation of this.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/gfp.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#ifndef CONFIG_USB_SERIAL_SAFE_PADDED
#define CONFIG_USB_SERIAL_SAFE_PADDED 0
#endif
static bool safe = 1;
static bool padded = CONFIG_USB_SERIAL_SAFE_PADDED;
#define DRIVER_AUTHOR "sl@lineo.com, tbr@lineo.com, Johan Hovold <jhovold@gmail.com>"
#define DRIVER_DESC "USB Safe Encapsulated Serial"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(safe, bool, 0);
MODULE_PARM_DESC(safe, "Turn Safe Encapsulation On/Off");
module_param(padded, bool, 0);
MODULE_PARM_DESC(padded, "Pad to full wMaxPacketSize On/Off");
#define CDC_DEVICE_CLASS 0x02
#define CDC_INTERFACE_CLASS 0x02
#define CDC_INTERFACE_SUBCLASS 0x06
#define LINEO_INTERFACE_CLASS 0xff
#define LINEO_INTERFACE_SUBCLASS_SAFENET 0x01
#define LINEO_SAFENET_CRC 0x01
#define LINEO_SAFENET_CRC_PADDED 0x02
#define LINEO_INTERFACE_SUBCLASS_SAFESERIAL 0x02
#define LINEO_SAFESERIAL_CRC 0x01
#define LINEO_SAFESERIAL_CRC_PADDED 0x02
#define MY_USB_DEVICE(vend, prod, dc, ic, isc) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_DEV_CLASS | \
USB_DEVICE_ID_MATCH_INT_CLASS | \
USB_DEVICE_ID_MATCH_INT_SUBCLASS, \
.idVendor = (vend), \
.idProduct = (prod),\
.bDeviceClass = (dc),\
.bInterfaceClass = (ic), \
.bInterfaceSubClass = (isc),
static const struct usb_device_id id_table[] = {
{MY_USB_DEVICE(0x49f, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Itsy */
{MY_USB_DEVICE(0x3f0, 0x2101, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Calypso */
{MY_USB_DEVICE(0x4dd, 0x8001, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Iris */
{MY_USB_DEVICE(0x4dd, 0x8002, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Collie */
{MY_USB_DEVICE(0x4dd, 0x8003, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Collie */
{MY_USB_DEVICE(0x4dd, 0x8004, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Collie */
{MY_USB_DEVICE(0x5f9, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Sharp tmp */
{} /* terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
static const __u16 crc10_table[256] = {
0x000, 0x233, 0x255, 0x066, 0x299, 0x0aa, 0x0cc, 0x2ff,
0x301, 0x132, 0x154, 0x367, 0x198, 0x3ab, 0x3cd, 0x1fe,
0x031, 0x202, 0x264, 0x057, 0x2a8, 0x09b, 0x0fd, 0x2ce,
0x330, 0x103, 0x165, 0x356, 0x1a9, 0x39a, 0x3fc, 0x1cf,
0x062, 0x251, 0x237, 0x004, 0x2fb, 0x0c8, 0x0ae, 0x29d,
0x363, 0x150, 0x136, 0x305, 0x1fa, 0x3c9, 0x3af, 0x19c,
0x053, 0x260, 0x206, 0x035, 0x2ca, 0x0f9, 0x09f, 0x2ac,
0x352, 0x161, 0x107, 0x334, 0x1cb, 0x3f8, 0x39e, 0x1ad,
0x0c4, 0x2f7, 0x291, 0x0a2, 0x25d, 0x06e, 0x008, 0x23b,
0x3c5, 0x1f6, 0x190, 0x3a3, 0x15c, 0x36f, 0x309, 0x13a,
0x0f5, 0x2c6, 0x2a0, 0x093, 0x26c, 0x05f, 0x039, 0x20a,
0x3f4, 0x1c7, 0x1a1, 0x392, 0x16d, 0x35e, 0x338, 0x10b,
0x0a6, 0x295, 0x2f3, 0x0c0, 0x23f, 0x00c, 0x06a, 0x259,
0x3a7, 0x194, 0x1f2, 0x3c1, 0x13e, 0x30d, 0x36b, 0x158,
0x097, 0x2a4, 0x2c2, 0x0f1, 0x20e, 0x03d, 0x05b, 0x268,
0x396, 0x1a5, 0x1c3, 0x3f0, 0x10f, 0x33c, 0x35a, 0x169,
0x188, 0x3bb, 0x3dd, 0x1ee, 0x311, 0x122, 0x144, 0x377,
0x289, 0x0ba, 0x0dc, 0x2ef, 0x010, 0x223, 0x245, 0x076,
0x1b9, 0x38a, 0x3ec, 0x1df, 0x320, 0x113, 0x175, 0x346,
0x2b8, 0x08b, 0x0ed, 0x2de, 0x021, 0x212, 0x274, 0x047,
0x1ea, 0x3d9, 0x3bf, 0x18c, 0x373, 0x140, 0x126, 0x315,
0x2eb, 0x0d8, 0x0be, 0x28d, 0x072, 0x241, 0x227, 0x014,
0x1db, 0x3e8, 0x38e, 0x1bd, 0x342, 0x171, 0x117, 0x324,
0x2da, 0x0e9, 0x08f, 0x2bc, 0x043, 0x270, 0x216, 0x025,
0x14c, 0x37f, 0x319, 0x12a, 0x3d5, 0x1e6, 0x180, 0x3b3,
0x24d, 0x07e, 0x018, 0x22b, 0x0d4, 0x2e7, 0x281, 0x0b2,
0x17d, 0x34e, 0x328, 0x11b, 0x3e4, 0x1d7, 0x1b1, 0x382,
0x27c, 0x04f, 0x029, 0x21a, 0x0e5, 0x2d6, 0x2b0, 0x083,
0x12e, 0x31d, 0x37b, 0x148, 0x3b7, 0x184, 0x1e2, 0x3d1,
0x22f, 0x01c, 0x07a, 0x249, 0x0b6, 0x285, 0x2e3, 0x0d0,
0x11f, 0x32c, 0x34a, 0x179, 0x386, 0x1b5, 0x1d3, 0x3e0,
0x21e, 0x02d, 0x04b, 0x278, 0x087, 0x2b4, 0x2d2, 0x0e1,
};
#define CRC10_INITFCS 0x000 /* Initial FCS value */
#define CRC10_GOODFCS 0x000 /* Good final FCS value */
#define CRC10_FCS(fcs, c) ((((fcs) << 8) & 0x3ff) ^ crc10_table[((fcs) >> 2) & 0xff] ^ (c))
/**
* fcs_compute10 - memcpy and calculate 10 bit CRC across buffer
* @sp: pointer to buffer
* @len: number of bytes
* @fcs: starting FCS
*
* Perform a memcpy and calculate fcs using ppp 10bit CRC algorithm. Return
* new 10 bit FCS.
*/
static __u16 __inline__ fcs_compute10(unsigned char *sp, int len, __u16 fcs)
{
for (; len-- > 0; fcs = CRC10_FCS(fcs, *sp++));
return fcs;
}
static void safe_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
unsigned char length = urb->actual_length;
int actual_length;
__u16 fcs;
if (!length)
return;
if (!safe)
goto out;
fcs = fcs_compute10(data, length, CRC10_INITFCS);
if (fcs) {
dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs);
return;
}
actual_length = data[length - 2] >> 2;
if (actual_length > (length - 2)) {
dev_err(&port->dev, "%s - inconsistent lengths %d:%d\n",
__func__, actual_length, length);
return;
}
dev_info(&urb->dev->dev, "%s - actual: %d\n", __func__, actual_length);
length = actual_length;
out:
tty_insert_flip_string(&port->port, data, length);
tty_flip_buffer_push(&port->port);
}
static int safe_prepare_write_buffer(struct usb_serial_port *port,
void *dest, size_t size)
{
unsigned char *buf = dest;
int count;
int trailer_len;
int pkt_len;
__u16 fcs;
trailer_len = safe ? 2 : 0;
count = kfifo_out_locked(&port->write_fifo, buf, size - trailer_len,
&port->lock);
if (!safe)
return count;
/* pad if necessary */
if (padded) {
pkt_len = size;
memset(buf + count, '0', pkt_len - count - trailer_len);
} else {
pkt_len = count + trailer_len;
}
/* set count */
buf[pkt_len - 2] = count << 2;
buf[pkt_len - 1] = 0;
/* compute fcs and insert into trailer */
fcs = fcs_compute10(buf, pkt_len, CRC10_INITFCS);
buf[pkt_len - 2] |= fcs >> 8;
buf[pkt_len - 1] |= fcs & 0xff;
return pkt_len;
}
static int safe_startup(struct usb_serial *serial)
{
struct usb_interface_descriptor *desc;
if (serial->dev->descriptor.bDeviceClass != CDC_DEVICE_CLASS)
return -ENODEV;
desc = &serial->interface->cur_altsetting->desc;
if (desc->bInterfaceClass != LINEO_INTERFACE_CLASS)
return -ENODEV;
if (desc->bInterfaceSubClass != LINEO_INTERFACE_SUBCLASS_SAFESERIAL)
return -ENODEV;
switch (desc->bInterfaceProtocol) {
case LINEO_SAFESERIAL_CRC:
break;
case LINEO_SAFESERIAL_CRC_PADDED:
padded = 1;
break;
default:
return -EINVAL;
}
return 0;
}
static struct usb_serial_driver safe_device = {
.driver = {
.owner = THIS_MODULE,
.name = "safe_serial",
},
.id_table = id_table,
.num_ports = 1,
.process_read_urb = safe_process_read_urb,
.prepare_write_buffer = safe_prepare_write_buffer,
.attach = safe_startup,
};
static struct usb_serial_driver * const serial_drivers[] = {
&safe_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);

1100
drivers/usb/serial/sierra.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,491 @@
/*
* spcp8x5 USB to serial adaptor driver
*
* Copyright (C) 2010-2013 Johan Hovold (jhovold@gmail.com)
* Copyright (C) 2006 Linxb (xubin.lin@worldplus.com.cn)
* Copyright (C) 2006 S1 Corp.
*
* Original driver for 2.6.10 pl2303 driver by
* Greg Kroah-Hartman (greg@kroah.com)
* Changes for 2.6.20 by Harald Klein <hari@vt100.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#define DRIVER_DESC "SPCP8x5 USB to serial adaptor driver"
#define SPCP825_QUIRK_NO_UART_STATUS 0x01
#define SPCP825_QUIRK_NO_WORK_MODE 0x02
#define SPCP8x5_007_VID 0x04FC
#define SPCP8x5_007_PID 0x0201
#define SPCP8x5_008_VID 0x04fc
#define SPCP8x5_008_PID 0x0235
#define SPCP8x5_PHILIPS_VID 0x0471
#define SPCP8x5_PHILIPS_PID 0x081e
#define SPCP8x5_INTERMATIC_VID 0x04FC
#define SPCP8x5_INTERMATIC_PID 0x0204
#define SPCP8x5_835_VID 0x04fc
#define SPCP8x5_835_PID 0x0231
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(SPCP8x5_PHILIPS_VID , SPCP8x5_PHILIPS_PID)},
{ USB_DEVICE(SPCP8x5_INTERMATIC_VID, SPCP8x5_INTERMATIC_PID)},
{ USB_DEVICE(SPCP8x5_835_VID, SPCP8x5_835_PID)},
{ USB_DEVICE(SPCP8x5_008_VID, SPCP8x5_008_PID)},
{ USB_DEVICE(SPCP8x5_007_VID, SPCP8x5_007_PID),
.driver_info = SPCP825_QUIRK_NO_UART_STATUS |
SPCP825_QUIRK_NO_WORK_MODE },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
struct spcp8x5_usb_ctrl_arg {
u8 type;
u8 cmd;
u8 cmd_type;
u16 value;
u16 index;
u16 length;
};
/* spcp8x5 spec register define */
#define MCR_CONTROL_LINE_RTS 0x02
#define MCR_CONTROL_LINE_DTR 0x01
#define MCR_DTR 0x01
#define MCR_RTS 0x02
#define MSR_STATUS_LINE_DCD 0x80
#define MSR_STATUS_LINE_RI 0x40
#define MSR_STATUS_LINE_DSR 0x20
#define MSR_STATUS_LINE_CTS 0x10
/* verdor command here , we should define myself */
#define SET_DEFAULT 0x40
#define SET_DEFAULT_TYPE 0x20
#define SET_UART_FORMAT 0x40
#define SET_UART_FORMAT_TYPE 0x21
#define SET_UART_FORMAT_SIZE_5 0x00
#define SET_UART_FORMAT_SIZE_6 0x01
#define SET_UART_FORMAT_SIZE_7 0x02
#define SET_UART_FORMAT_SIZE_8 0x03
#define SET_UART_FORMAT_STOP_1 0x00
#define SET_UART_FORMAT_STOP_2 0x04
#define SET_UART_FORMAT_PAR_NONE 0x00
#define SET_UART_FORMAT_PAR_ODD 0x10
#define SET_UART_FORMAT_PAR_EVEN 0x30
#define SET_UART_FORMAT_PAR_MASK 0xD0
#define SET_UART_FORMAT_PAR_SPACE 0x90
#define GET_UART_STATUS_TYPE 0xc0
#define GET_UART_STATUS 0x22
#define GET_UART_STATUS_MSR 0x06
#define SET_UART_STATUS 0x40
#define SET_UART_STATUS_TYPE 0x23
#define SET_UART_STATUS_MCR 0x0004
#define SET_UART_STATUS_MCR_DTR 0x01
#define SET_UART_STATUS_MCR_RTS 0x02
#define SET_UART_STATUS_MCR_LOOP 0x10
#define SET_WORKING_MODE 0x40
#define SET_WORKING_MODE_TYPE 0x24
#define SET_WORKING_MODE_U2C 0x00
#define SET_WORKING_MODE_RS485 0x01
#define SET_WORKING_MODE_PDMA 0x02
#define SET_WORKING_MODE_SPP 0x03
#define SET_FLOWCTL_CHAR 0x40
#define SET_FLOWCTL_CHAR_TYPE 0x25
#define GET_VERSION 0xc0
#define GET_VERSION_TYPE 0x26
#define SET_REGISTER 0x40
#define SET_REGISTER_TYPE 0x27
#define GET_REGISTER 0xc0
#define GET_REGISTER_TYPE 0x28
#define SET_RAM 0x40
#define SET_RAM_TYPE 0x31
#define GET_RAM 0xc0
#define GET_RAM_TYPE 0x32
/* how come ??? */
#define UART_STATE 0x08
#define UART_STATE_TRANSIENT_MASK 0x75
#define UART_DCD 0x01
#define UART_DSR 0x02
#define UART_BREAK_ERROR 0x04
#define UART_RING 0x08
#define UART_FRAME_ERROR 0x10
#define UART_PARITY_ERROR 0x20
#define UART_OVERRUN_ERROR 0x40
#define UART_CTS 0x80
struct spcp8x5_private {
unsigned quirks;
spinlock_t lock;
u8 line_control;
};
static int spcp8x5_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
usb_set_serial_data(serial, (void *)id);
return 0;
}
static int spcp8x5_port_probe(struct usb_serial_port *port)
{
const struct usb_device_id *id = usb_get_serial_data(port->serial);
struct spcp8x5_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
priv->quirks = id->driver_info;
usb_set_serial_port_data(port, priv);
port->port.drain_delay = 256;
return 0;
}
static int spcp8x5_port_remove(struct usb_serial_port *port)
{
struct spcp8x5_private *priv;
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static int spcp8x5_set_ctrl_line(struct usb_serial_port *port, u8 mcr)
{
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
struct usb_device *dev = port->serial->dev;
int retval;
if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS)
return -EPERM;
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
SET_UART_STATUS_TYPE, SET_UART_STATUS,
mcr, 0x04, NULL, 0, 100);
if (retval != 0) {
dev_err(&port->dev, "failed to set control lines: %d\n",
retval);
}
return retval;
}
static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status)
{
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
struct usb_device *dev = port->serial->dev;
u8 *buf;
int ret;
if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS)
return -EPERM;
buf = kzalloc(1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
GET_UART_STATUS, GET_UART_STATUS_TYPE,
0, GET_UART_STATUS_MSR, buf, 1, 100);
if (ret < 0)
dev_err(&port->dev, "failed to get modem status: %d\n", ret);
dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x\n", ret, *buf);
*status = *buf;
kfree(buf);
return ret;
}
static void spcp8x5_set_work_mode(struct usb_serial_port *port, u16 value,
u16 index)
{
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
struct usb_device *dev = port->serial->dev;
int ret;
if (priv->quirks & SPCP825_QUIRK_NO_WORK_MODE)
return;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
SET_WORKING_MODE_TYPE, SET_WORKING_MODE,
value, index, NULL, 0, 100);
dev_dbg(&port->dev, "value = %#x , index = %#x\n", value, index);
if (ret < 0)
dev_err(&port->dev, "failed to set work mode: %d\n", ret);
}
static int spcp8x5_carrier_raised(struct usb_serial_port *port)
{
u8 msr;
int ret;
ret = spcp8x5_get_msr(port, &msr);
if (ret || msr & MSR_STATUS_LINE_DCD)
return 1;
return 0;
}
static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on)
{
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
u8 control;
spin_lock_irqsave(&priv->lock, flags);
if (on)
priv->line_control = MCR_CONTROL_LINE_DTR
| MCR_CONTROL_LINE_RTS;
else
priv->line_control &= ~ (MCR_CONTROL_LINE_DTR
| MCR_CONTROL_LINE_RTS);
control = priv->line_control;
spin_unlock_irqrestore(&priv->lock, flags);
spcp8x5_set_ctrl_line(port, control);
}
static void spcp8x5_init_termios(struct tty_struct *tty)
{
tty->termios = tty_std_termios;
tty->termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
tty->termios.c_ispeed = 115200;
tty->termios.c_ospeed = 115200;
}
static void spcp8x5_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct usb_serial *serial = port->serial;
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
unsigned int cflag = tty->termios.c_cflag;
unsigned short uartdata;
unsigned char buf[2] = {0, 0};
int baud;
int i;
u8 control;
/* check that they really want us to change something */
if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
return;
/* set DTR/RTS active */
spin_lock_irqsave(&priv->lock, flags);
control = priv->line_control;
if (old_termios && (old_termios->c_cflag & CBAUD) == B0) {
priv->line_control |= MCR_DTR;
if (!(old_termios->c_cflag & CRTSCTS))
priv->line_control |= MCR_RTS;
}
if (control != priv->line_control) {
control = priv->line_control;
spin_unlock_irqrestore(&priv->lock, flags);
spcp8x5_set_ctrl_line(port, control);
} else {
spin_unlock_irqrestore(&priv->lock, flags);
}
/* Set Baud Rate */
baud = tty_get_baud_rate(tty);
switch (baud) {
case 300: buf[0] = 0x00; break;
case 600: buf[0] = 0x01; break;
case 1200: buf[0] = 0x02; break;
case 2400: buf[0] = 0x03; break;
case 4800: buf[0] = 0x04; break;
case 9600: buf[0] = 0x05; break;
case 19200: buf[0] = 0x07; break;
case 38400: buf[0] = 0x09; break;
case 57600: buf[0] = 0x0a; break;
case 115200: buf[0] = 0x0b; break;
case 230400: buf[0] = 0x0c; break;
case 460800: buf[0] = 0x0d; break;
case 921600: buf[0] = 0x0e; break;
/* case 1200000: buf[0] = 0x0f; break; */
/* case 2400000: buf[0] = 0x10; break; */
case 3000000: buf[0] = 0x11; break;
/* case 6000000: buf[0] = 0x12; break; */
case 0:
case 1000000:
buf[0] = 0x0b; break;
default:
dev_err(&port->dev, "unsupported baudrate, using 9600\n");
}
/* Set Data Length : 00:5bit, 01:6bit, 10:7bit, 11:8bit */
switch (cflag & CSIZE) {
case CS5:
buf[1] |= SET_UART_FORMAT_SIZE_5;
break;
case CS6:
buf[1] |= SET_UART_FORMAT_SIZE_6;
break;
case CS7:
buf[1] |= SET_UART_FORMAT_SIZE_7;
break;
default:
case CS8:
buf[1] |= SET_UART_FORMAT_SIZE_8;
break;
}
/* Set Stop bit2 : 0:1bit 1:2bit */
buf[1] |= (cflag & CSTOPB) ? SET_UART_FORMAT_STOP_2 :
SET_UART_FORMAT_STOP_1;
/* Set Parity bit3-4 01:Odd 11:Even */
if (cflag & PARENB) {
buf[1] |= (cflag & PARODD) ?
SET_UART_FORMAT_PAR_ODD : SET_UART_FORMAT_PAR_EVEN ;
} else {
buf[1] |= SET_UART_FORMAT_PAR_NONE;
}
uartdata = buf[0] | buf[1]<<8;
i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
SET_UART_FORMAT_TYPE, SET_UART_FORMAT,
uartdata, 0, NULL, 0, 100);
if (i < 0)
dev_err(&port->dev, "Set UART format %#x failed (error = %d)\n",
uartdata, i);
dev_dbg(&port->dev, "0x21:0x40:0:0 %d\n", i);
if (cflag & CRTSCTS) {
/* enable hardware flow control */
spcp8x5_set_work_mode(port, 0x000a, SET_WORKING_MODE_U2C);
}
}
static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
int ret;
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
0x09, 0x00,
0x01, 0x00, NULL, 0x00, 100);
if (ret)
return ret;
spcp8x5_set_ctrl_line(port, priv->line_control);
if (tty)
spcp8x5_set_termios(tty, port, NULL);
return usb_serial_generic_open(tty, port);
}
static int spcp8x5_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
u8 control;
spin_lock_irqsave(&priv->lock, flags);
if (set & TIOCM_RTS)
priv->line_control |= MCR_RTS;
if (set & TIOCM_DTR)
priv->line_control |= MCR_DTR;
if (clear & TIOCM_RTS)
priv->line_control &= ~MCR_RTS;
if (clear & TIOCM_DTR)
priv->line_control &= ~MCR_DTR;
control = priv->line_control;
spin_unlock_irqrestore(&priv->lock, flags);
return spcp8x5_set_ctrl_line(port, control);
}
static int spcp8x5_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
unsigned int mcr;
u8 status;
unsigned int result;
result = spcp8x5_get_msr(port, &status);
if (result)
return result;
spin_lock_irqsave(&priv->lock, flags);
mcr = priv->line_control;
spin_unlock_irqrestore(&priv->lock, flags);
result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
| ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
| ((status & MSR_STATUS_LINE_CTS) ? TIOCM_CTS : 0)
| ((status & MSR_STATUS_LINE_DSR) ? TIOCM_DSR : 0)
| ((status & MSR_STATUS_LINE_RI) ? TIOCM_RI : 0)
| ((status & MSR_STATUS_LINE_DCD) ? TIOCM_CD : 0);
return result;
}
static struct usb_serial_driver spcp8x5_device = {
.driver = {
.owner = THIS_MODULE,
.name = "SPCP8x5",
},
.id_table = id_table,
.num_ports = 1,
.open = spcp8x5_open,
.dtr_rts = spcp8x5_dtr_rts,
.carrier_raised = spcp8x5_carrier_raised,
.set_termios = spcp8x5_set_termios,
.init_termios = spcp8x5_init_termios,
.tiocmget = spcp8x5_tiocmget,
.tiocmset = spcp8x5_tiocmset,
.probe = spcp8x5_probe,
.port_probe = spcp8x5_port_probe,
.port_remove = spcp8x5_port_remove,
};
static struct usb_serial_driver * const serial_drivers[] = {
&spcp8x5_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

566
drivers/usb/serial/ssu100.c Normal file
View file

@ -0,0 +1,566 @@
/*
* usb-serial driver for Quatech SSU-100
*
* based on ftdi_sio.c and the original serqt_usb.c from Quatech
*
*/
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/serial.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial_reg.h>
#include <linux/uaccess.h>
#define QT_OPEN_CLOSE_CHANNEL 0xca
#define QT_SET_GET_DEVICE 0xc2
#define QT_SET_GET_REGISTER 0xc0
#define QT_GET_SET_PREBUF_TRIG_LVL 0xcc
#define QT_SET_ATF 0xcd
#define QT_GET_SET_UART 0xc1
#define QT_TRANSFER_IN 0xc0
#define QT_HW_FLOW_CONTROL_MASK 0xc5
#define QT_SW_FLOW_CONTROL_MASK 0xc6
#define SERIAL_MSR_MASK 0xf0
#define SERIAL_CRTSCTS ((UART_MCR_RTS << 8) | UART_MSR_CTS)
#define SERIAL_EVEN_PARITY (UART_LCR_PARITY | UART_LCR_EPAR)
#define MAX_BAUD_RATE 460800
#define ATC_DISABLED 0x00
#define DUPMODE_BITS 0xc0
#define RR_BITS 0x03
#define LOOPMODE_BITS 0x41
#define RS232_MODE 0x00
#define RTSCTS_TO_CONNECTOR 0x40
#define CLKS_X4 0x02
#define FULLPWRBIT 0x00000080
#define NEXT_BOARD_POWER_BIT 0x00000004
#define DRIVER_DESC "Quatech SSU-100 USB to Serial Driver"
#define USB_VENDOR_ID_QUATECH 0x061d /* Quatech VID */
#define QUATECH_SSU100 0xC020 /* SSU100 */
static const struct usb_device_id id_table[] = {
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_SSU100)},
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
struct ssu100_port_private {
spinlock_t status_lock;
u8 shadowLSR;
u8 shadowMSR;
};
static inline int ssu100_control_msg(struct usb_device *dev,
u8 request, u16 data, u16 index)
{
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
request, 0x40, data, index,
NULL, 0, 300);
}
static inline int ssu100_setdevice(struct usb_device *dev, u8 *data)
{
u16 x = ((u16)(data[1] << 8) | (u16)(data[0]));
return ssu100_control_msg(dev, QT_SET_GET_DEVICE, x, 0);
}
static inline int ssu100_getdevice(struct usb_device *dev, u8 *data)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
QT_SET_GET_DEVICE, 0xc0, 0, 0,
data, 3, 300);
}
static inline int ssu100_getregister(struct usb_device *dev,
unsigned short uart,
unsigned short reg,
u8 *data)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
QT_SET_GET_REGISTER, 0xc0, reg,
uart, data, sizeof(*data), 300);
}
static inline int ssu100_setregister(struct usb_device *dev,
unsigned short uart,
unsigned short reg,
u16 data)
{
u16 value = (data << 8) | reg;
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
QT_SET_GET_REGISTER, 0x40, value, uart,
NULL, 0, 300);
}
#define set_mctrl(dev, set) update_mctrl((dev), (set), 0)
#define clear_mctrl(dev, clear) update_mctrl((dev), 0, (clear))
/* these do not deal with device that have more than 1 port */
static inline int update_mctrl(struct usb_device *dev, unsigned int set,
unsigned int clear)
{
unsigned urb_value;
int result;
if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
dev_dbg(&dev->dev, "%s - DTR|RTS not being set|cleared\n", __func__);
return 0; /* no change */
}
clear &= ~set; /* 'set' takes precedence over 'clear' */
urb_value = 0;
if (set & TIOCM_DTR)
urb_value |= UART_MCR_DTR;
if (set & TIOCM_RTS)
urb_value |= UART_MCR_RTS;
result = ssu100_setregister(dev, 0, UART_MCR, urb_value);
if (result < 0)
dev_dbg(&dev->dev, "%s Error from MODEM_CTRL urb\n", __func__);
return result;
}
static int ssu100_initdevice(struct usb_device *dev)
{
u8 *data;
int result = 0;
data = kzalloc(3, GFP_KERNEL);
if (!data)
return -ENOMEM;
result = ssu100_getdevice(dev, data);
if (result < 0) {
dev_dbg(&dev->dev, "%s - get_device failed %i\n", __func__, result);
goto out;
}
data[1] &= ~FULLPWRBIT;
result = ssu100_setdevice(dev, data);
if (result < 0) {
dev_dbg(&dev->dev, "%s - setdevice failed %i\n", __func__, result);
goto out;
}
result = ssu100_control_msg(dev, QT_GET_SET_PREBUF_TRIG_LVL, 128, 0);
if (result < 0) {
dev_dbg(&dev->dev, "%s - set prebuffer level failed %i\n", __func__, result);
goto out;
}
result = ssu100_control_msg(dev, QT_SET_ATF, ATC_DISABLED, 0);
if (result < 0) {
dev_dbg(&dev->dev, "%s - set ATFprebuffer level failed %i\n", __func__, result);
goto out;
}
result = ssu100_getdevice(dev, data);
if (result < 0) {
dev_dbg(&dev->dev, "%s - get_device failed %i\n", __func__, result);
goto out;
}
data[0] &= ~(RR_BITS | DUPMODE_BITS);
data[0] |= CLKS_X4;
data[1] &= ~(LOOPMODE_BITS);
data[1] |= RS232_MODE;
result = ssu100_setdevice(dev, data);
if (result < 0) {
dev_dbg(&dev->dev, "%s - setdevice failed %i\n", __func__, result);
goto out;
}
out: kfree(data);
return result;
}
static void ssu100_set_termios(struct tty_struct *tty,
struct usb_serial_port *port,
struct ktermios *old_termios)
{
struct usb_device *dev = port->serial->dev;
struct ktermios *termios = &tty->termios;
u16 baud, divisor, remainder;
unsigned int cflag = termios->c_cflag;
u16 urb_value = 0; /* will hold the new flags */
int result;
if (cflag & PARENB) {
if (cflag & PARODD)
urb_value |= UART_LCR_PARITY;
else
urb_value |= SERIAL_EVEN_PARITY;
}
switch (cflag & CSIZE) {
case CS5:
urb_value |= UART_LCR_WLEN5;
break;
case CS6:
urb_value |= UART_LCR_WLEN6;
break;
case CS7:
urb_value |= UART_LCR_WLEN7;
break;
default:
case CS8:
urb_value |= UART_LCR_WLEN8;
break;
}
baud = tty_get_baud_rate(tty);
if (!baud)
baud = 9600;
dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud);
divisor = MAX_BAUD_RATE / baud;
remainder = MAX_BAUD_RATE % baud;
if (((remainder * 2) >= baud) && (baud != 110))
divisor++;
urb_value = urb_value << 8;
result = ssu100_control_msg(dev, QT_GET_SET_UART, divisor, urb_value);
if (result < 0)
dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
if (cflag & CRTSCTS)
result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
SERIAL_CRTSCTS, 0);
else
result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
0, 0);
if (result < 0)
dev_dbg(&port->dev, "%s - set HW flow control failed\n", __func__);
if (I_IXOFF(tty) || I_IXON(tty)) {
u16 x = ((u16)(START_CHAR(tty) << 8) | (u16)(STOP_CHAR(tty)));
result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
x, 0);
} else
result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
0, 0);
if (result < 0)
dev_dbg(&port->dev, "%s - set SW flow control failed\n", __func__);
}
static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_device *dev = port->serial->dev;
struct ssu100_port_private *priv = usb_get_serial_port_data(port);
u8 *data;
int result;
unsigned long flags;
data = kzalloc(2, GFP_KERNEL);
if (!data)
return -ENOMEM;
result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
QT_OPEN_CLOSE_CHANNEL,
QT_TRANSFER_IN, 0x01,
0, data, 2, 300);
if (result < 0) {
dev_dbg(&port->dev, "%s - open failed %i\n", __func__, result);
kfree(data);
return result;
}
spin_lock_irqsave(&priv->status_lock, flags);
priv->shadowLSR = data[0];
priv->shadowMSR = data[1];
spin_unlock_irqrestore(&priv->status_lock, flags);
kfree(data);
/* set to 9600 */
result = ssu100_control_msg(dev, QT_GET_SET_UART, 0x30, 0x0300);
if (result < 0)
dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
if (tty)
ssu100_set_termios(tty, port, &tty->termios);
return usb_serial_generic_open(tty, port);
}
static int get_serial_info(struct usb_serial_port *port,
struct serial_struct __user *retinfo)
{
struct serial_struct tmp;
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.line = port->minor;
tmp.port = 0;
tmp.irq = 0;
tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = port->bulk_out_size;
tmp.baud_base = 9600;
tmp.close_delay = 5*HZ;
tmp.closing_wait = 30*HZ;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
}
static int ssu100_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
switch (cmd) {
case TIOCGSERIAL:
return get_serial_info(port,
(struct serial_struct __user *) arg);
default:
break;
}
return -ENOIOCTLCMD;
}
static int ssu100_attach(struct usb_serial *serial)
{
return ssu100_initdevice(serial->dev);
}
static int ssu100_port_probe(struct usb_serial_port *port)
{
struct ssu100_port_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->status_lock);
usb_set_serial_port_data(port, priv);
return 0;
}
static int ssu100_port_remove(struct usb_serial_port *port)
{
struct ssu100_port_private *priv;
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static int ssu100_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_device *dev = port->serial->dev;
u8 *d;
int r;
d = kzalloc(2, GFP_KERNEL);
if (!d)
return -ENOMEM;
r = ssu100_getregister(dev, 0, UART_MCR, d);
if (r < 0)
goto mget_out;
r = ssu100_getregister(dev, 0, UART_MSR, d+1);
if (r < 0)
goto mget_out;
r = (d[0] & UART_MCR_DTR ? TIOCM_DTR : 0) |
(d[0] & UART_MCR_RTS ? TIOCM_RTS : 0) |
(d[1] & UART_MSR_CTS ? TIOCM_CTS : 0) |
(d[1] & UART_MSR_DCD ? TIOCM_CAR : 0) |
(d[1] & UART_MSR_RI ? TIOCM_RI : 0) |
(d[1] & UART_MSR_DSR ? TIOCM_DSR : 0);
mget_out:
kfree(d);
return r;
}
static int ssu100_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_device *dev = port->serial->dev;
return update_mctrl(dev, set, clear);
}
static void ssu100_dtr_rts(struct usb_serial_port *port, int on)
{
struct usb_device *dev = port->serial->dev;
/* Disable flow control */
if (!on) {
if (ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
dev_err(&port->dev, "error from flowcontrol urb\n");
}
/* drop RTS and DTR */
if (on)
set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
else
clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
}
static void ssu100_update_msr(struct usb_serial_port *port, u8 msr)
{
struct ssu100_port_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
spin_lock_irqsave(&priv->status_lock, flags);
priv->shadowMSR = msr;
spin_unlock_irqrestore(&priv->status_lock, flags);
if (msr & UART_MSR_ANY_DELTA) {
/* update input line counters */
if (msr & UART_MSR_DCTS)
port->icount.cts++;
if (msr & UART_MSR_DDSR)
port->icount.dsr++;
if (msr & UART_MSR_DDCD)
port->icount.dcd++;
if (msr & UART_MSR_TERI)
port->icount.rng++;
wake_up_interruptible(&port->port.delta_msr_wait);
}
}
static void ssu100_update_lsr(struct usb_serial_port *port, u8 lsr,
char *tty_flag)
{
struct ssu100_port_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
spin_lock_irqsave(&priv->status_lock, flags);
priv->shadowLSR = lsr;
spin_unlock_irqrestore(&priv->status_lock, flags);
*tty_flag = TTY_NORMAL;
if (lsr & UART_LSR_BRK_ERROR_BITS) {
/* we always want to update icount, but we only want to
* update tty_flag for one case */
if (lsr & UART_LSR_BI) {
port->icount.brk++;
*tty_flag = TTY_BREAK;
usb_serial_handle_break(port);
}
if (lsr & UART_LSR_PE) {
port->icount.parity++;
if (*tty_flag == TTY_NORMAL)
*tty_flag = TTY_PARITY;
}
if (lsr & UART_LSR_FE) {
port->icount.frame++;
if (*tty_flag == TTY_NORMAL)
*tty_flag = TTY_FRAME;
}
if (lsr & UART_LSR_OE) {
port->icount.overrun++;
tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
}
}
}
static void ssu100_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
char *packet = (char *)urb->transfer_buffer;
char flag = TTY_NORMAL;
u32 len = urb->actual_length;
int i;
char *ch;
if ((len >= 4) &&
(packet[0] == 0x1b) && (packet[1] == 0x1b) &&
((packet[2] == 0x00) || (packet[2] == 0x01))) {
if (packet[2] == 0x00)
ssu100_update_lsr(port, packet[3], &flag);
if (packet[2] == 0x01)
ssu100_update_msr(port, packet[3]);
len -= 4;
ch = packet + 4;
} else
ch = packet;
if (!len)
return; /* status only */
if (port->port.console && port->sysrq) {
for (i = 0; i < len; i++, ch++) {
if (!usb_serial_handle_sysrq_char(port, *ch))
tty_insert_flip_char(&port->port, *ch, flag);
}
} else
tty_insert_flip_string_fixed_flag(&port->port, ch, flag, len);
tty_flip_buffer_push(&port->port);
}
static struct usb_serial_driver ssu100_device = {
.driver = {
.owner = THIS_MODULE,
.name = "ssu100",
},
.description = DRIVER_DESC,
.id_table = id_table,
.num_ports = 1,
.open = ssu100_open,
.attach = ssu100_attach,
.port_probe = ssu100_port_probe,
.port_remove = ssu100_port_remove,
.dtr_rts = ssu100_dtr_rts,
.process_read_urb = ssu100_process_read_urb,
.tiocmget = ssu100_tiocmget,
.tiocmset = ssu100_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.ioctl = ssu100_ioctl,
.set_termios = ssu100_set_termios,
};
static struct usb_serial_driver * const serial_drivers[] = {
&ssu100_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,209 @@
/*
* Symbol USB barcode to serial driver
*
* Copyright (C) 2013 Johan Hovold <jhovold@gmail.com>
* Copyright (C) 2009 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (C) 2009 Novell Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x05e0, 0x0600) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
struct symbol_private {
spinlock_t lock; /* protects the following flags */
bool throttled;
bool actually_throttled;
};
static void symbol_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct symbol_private *priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
int status = urb->status;
int result;
int data_length;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
__func__, status);
goto exit;
}
usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
if (urb->actual_length > 1) {
data_length = urb->actual_length - 1;
/*
* Data from the device comes with a 1 byte header:
*
* <size of data>data...
* This is real data to be sent to the tty layer
* we pretty much just ignore the size and send everything
* else to the tty layer.
*/
tty_insert_flip_string(&port->port, &data[1], data_length);
tty_flip_buffer_push(&port->port);
} else {
dev_dbg(&port->dev, "%s - short packet\n", __func__);
}
exit:
spin_lock(&priv->lock);
/* Continue trying to always read if we should */
if (!priv->throttled) {
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
if (result)
dev_err(&port->dev,
"%s - failed resubmitting read urb, error %d\n",
__func__, result);
} else
priv->actually_throttled = true;
spin_unlock(&priv->lock);
}
static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct symbol_private *priv = usb_get_serial_data(port->serial);
unsigned long flags;
int result = 0;
spin_lock_irqsave(&priv->lock, flags);
priv->throttled = false;
priv->actually_throttled = false;
spin_unlock_irqrestore(&priv->lock, flags);
/* Start reading from the device */
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result)
dev_err(&port->dev,
"%s - failed resubmitting read urb, error %d\n",
__func__, result);
return result;
}
static void symbol_close(struct usb_serial_port *port)
{
usb_kill_urb(port->interrupt_in_urb);
}
static void symbol_throttle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct symbol_private *priv = usb_get_serial_data(port->serial);
spin_lock_irq(&priv->lock);
priv->throttled = true;
spin_unlock_irq(&priv->lock);
}
static void symbol_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct symbol_private *priv = usb_get_serial_data(port->serial);
int result;
bool was_throttled;
spin_lock_irq(&priv->lock);
priv->throttled = false;
was_throttled = priv->actually_throttled;
priv->actually_throttled = false;
spin_unlock_irq(&priv->lock);
if (was_throttled) {
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result)
dev_err(&port->dev,
"%s - failed submitting read urb, error %d\n",
__func__, result);
}
}
static int symbol_startup(struct usb_serial *serial)
{
if (!serial->num_interrupt_in) {
dev_err(&serial->dev->dev, "no interrupt-in endpoint\n");
return -ENODEV;
}
return 0;
}
static int symbol_port_probe(struct usb_serial_port *port)
{
struct symbol_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
usb_set_serial_port_data(port, priv);
return 0;
}
static int symbol_port_remove(struct usb_serial_port *port)
{
struct symbol_private *priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static struct usb_serial_driver symbol_device = {
.driver = {
.owner = THIS_MODULE,
.name = "symbol",
},
.id_table = id_table,
.num_ports = 1,
.attach = symbol_startup,
.port_probe = symbol_port_probe,
.port_remove = symbol_port_remove,
.open = symbol_open,
.close = symbol_close,
.throttle = symbol_throttle,
.unthrottle = symbol_unthrottle,
.read_int_callback = symbol_int_callback,
};
static struct usb_serial_driver * const serial_drivers[] = {
&symbol_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,247 @@
/* vi: ts=8 sw=8
*
* TI 3410/5052 USB Serial Driver Header
*
* Copyright (C) 2004 Texas Instruments
*
* This driver is based on the Linux io_ti driver, which is
* Copyright (C) 2000-2002 Inside Out Networks
* Copyright (C) 2001-2002 Greg Kroah-Hartman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* For questions or problems with this driver, contact Texas Instruments
* technical support, or Al Borchers <alborchers@steinerpoint.com>, or
* Peter Berger <pberger@brimson.com>.
*/
#ifndef _TI_3410_5052_H_
#define _TI_3410_5052_H_
/* Configuration ids */
#define TI_BOOT_CONFIG 1
#define TI_ACTIVE_CONFIG 2
/* Vendor and product ids */
#define TI_VENDOR_ID 0x0451
#define IBM_VENDOR_ID 0x04b3
#define TI_3410_PRODUCT_ID 0x3410
#define IBM_4543_PRODUCT_ID 0x4543
#define IBM_454B_PRODUCT_ID 0x454b
#define IBM_454C_PRODUCT_ID 0x454c
#define TI_3410_EZ430_ID 0xF430 /* TI ez430 development tool */
#define TI_5052_BOOT_PRODUCT_ID 0x5052 /* no EEPROM, no firmware */
#define TI_5152_BOOT_PRODUCT_ID 0x5152 /* no EEPROM, no firmware */
#define TI_5052_EEPROM_PRODUCT_ID 0x505A /* EEPROM, no firmware */
#define TI_5052_FIRMWARE_PRODUCT_ID 0x505F /* firmware is running */
#define FRI2_PRODUCT_ID 0x5053 /* Fish River Island II */
/* Multi-Tech vendor and product ids */
#define MTS_VENDOR_ID 0x06E0
#define MTS_GSM_NO_FW_PRODUCT_ID 0xF108
#define MTS_CDMA_NO_FW_PRODUCT_ID 0xF109
#define MTS_CDMA_PRODUCT_ID 0xF110
#define MTS_GSM_PRODUCT_ID 0xF111
#define MTS_EDGE_PRODUCT_ID 0xF112
#define MTS_MT9234MU_PRODUCT_ID 0xF114
#define MTS_MT9234ZBA_PRODUCT_ID 0xF115
#define MTS_MT9234ZBAOLD_PRODUCT_ID 0x0319
/* Abbott Diabetics vendor and product ids */
#define ABBOTT_VENDOR_ID 0x1a61
#define ABBOTT_STEREO_PLUG_ID 0x3410
#define ABBOTT_PRODUCT_ID ABBOTT_STEREO_PLUG_ID
#define ABBOTT_STRIP_PORT_ID 0x3420
/* Commands */
#define TI_GET_VERSION 0x01
#define TI_GET_PORT_STATUS 0x02
#define TI_GET_PORT_DEV_INFO 0x03
#define TI_GET_CONFIG 0x04
#define TI_SET_CONFIG 0x05
#define TI_OPEN_PORT 0x06
#define TI_CLOSE_PORT 0x07
#define TI_START_PORT 0x08
#define TI_STOP_PORT 0x09
#define TI_TEST_PORT 0x0A
#define TI_PURGE_PORT 0x0B
#define TI_RESET_EXT_DEVICE 0x0C
#define TI_WRITE_DATA 0x80
#define TI_READ_DATA 0x81
#define TI_REQ_TYPE_CLASS 0x82
/* Module identifiers */
#define TI_I2C_PORT 0x01
#define TI_IEEE1284_PORT 0x02
#define TI_UART1_PORT 0x03
#define TI_UART2_PORT 0x04
#define TI_RAM_PORT 0x05
/* Modem status */
#define TI_MSR_DELTA_CTS 0x01
#define TI_MSR_DELTA_DSR 0x02
#define TI_MSR_DELTA_RI 0x04
#define TI_MSR_DELTA_CD 0x08
#define TI_MSR_CTS 0x10
#define TI_MSR_DSR 0x20
#define TI_MSR_RI 0x40
#define TI_MSR_CD 0x80
#define TI_MSR_DELTA_MASK 0x0F
#define TI_MSR_MASK 0xF0
/* Line status */
#define TI_LSR_OVERRUN_ERROR 0x01
#define TI_LSR_PARITY_ERROR 0x02
#define TI_LSR_FRAMING_ERROR 0x04
#define TI_LSR_BREAK 0x08
#define TI_LSR_ERROR 0x0F
#define TI_LSR_RX_FULL 0x10
#define TI_LSR_TX_EMPTY 0x20
/* Line control */
#define TI_LCR_BREAK 0x40
/* Modem control */
#define TI_MCR_LOOP 0x04
#define TI_MCR_DTR 0x10
#define TI_MCR_RTS 0x20
/* Mask settings */
#define TI_UART_ENABLE_RTS_IN 0x0001
#define TI_UART_DISABLE_RTS 0x0002
#define TI_UART_ENABLE_PARITY_CHECKING 0x0008
#define TI_UART_ENABLE_DSR_OUT 0x0010
#define TI_UART_ENABLE_CTS_OUT 0x0020
#define TI_UART_ENABLE_X_OUT 0x0040
#define TI_UART_ENABLE_XA_OUT 0x0080
#define TI_UART_ENABLE_X_IN 0x0100
#define TI_UART_ENABLE_DTR_IN 0x0800
#define TI_UART_DISABLE_DTR 0x1000
#define TI_UART_ENABLE_MS_INTS 0x2000
#define TI_UART_ENABLE_AUTO_START_DMA 0x4000
/* Parity */
#define TI_UART_NO_PARITY 0x00
#define TI_UART_ODD_PARITY 0x01
#define TI_UART_EVEN_PARITY 0x02
#define TI_UART_MARK_PARITY 0x03
#define TI_UART_SPACE_PARITY 0x04
/* Stop bits */
#define TI_UART_1_STOP_BITS 0x00
#define TI_UART_1_5_STOP_BITS 0x01
#define TI_UART_2_STOP_BITS 0x02
/* Bits per character */
#define TI_UART_5_DATA_BITS 0x00
#define TI_UART_6_DATA_BITS 0x01
#define TI_UART_7_DATA_BITS 0x02
#define TI_UART_8_DATA_BITS 0x03
/* 232/485 modes */
#define TI_UART_232 0x00
#define TI_UART_485_RECEIVER_DISABLED 0x01
#define TI_UART_485_RECEIVER_ENABLED 0x02
/* Pipe transfer mode and timeout */
#define TI_PIPE_MODE_CONTINOUS 0x01
#define TI_PIPE_MODE_MASK 0x03
#define TI_PIPE_TIMEOUT_MASK 0x7C
#define TI_PIPE_TIMEOUT_ENABLE 0x80
/* Config struct */
struct ti_uart_config {
__u16 wBaudRate;
__u16 wFlags;
__u8 bDataBits;
__u8 bParity;
__u8 bStopBits;
char cXon;
char cXoff;
__u8 bUartMode;
} __attribute__((packed));
/* Get port status */
struct ti_port_status {
__u8 bCmdCode;
__u8 bModuleId;
__u8 bErrorCode;
__u8 bMSR;
__u8 bLSR;
} __attribute__((packed));
/* Purge modes */
#define TI_PURGE_OUTPUT 0x00
#define TI_PURGE_INPUT 0x80
/* Read/Write data */
#define TI_RW_DATA_ADDR_SFR 0x10
#define TI_RW_DATA_ADDR_IDATA 0x20
#define TI_RW_DATA_ADDR_XDATA 0x30
#define TI_RW_DATA_ADDR_CODE 0x40
#define TI_RW_DATA_ADDR_GPIO 0x50
#define TI_RW_DATA_ADDR_I2C 0x60
#define TI_RW_DATA_ADDR_FLASH 0x70
#define TI_RW_DATA_ADDR_DSP 0x80
#define TI_RW_DATA_UNSPECIFIED 0x00
#define TI_RW_DATA_BYTE 0x01
#define TI_RW_DATA_WORD 0x02
#define TI_RW_DATA_DOUBLE_WORD 0x04
struct ti_write_data_bytes {
__u8 bAddrType;
__u8 bDataType;
__u8 bDataCounter;
__be16 wBaseAddrHi;
__be16 wBaseAddrLo;
__u8 bData[0];
} __attribute__((packed));
struct ti_read_data_request {
__u8 bAddrType;
__u8 bDataType;
__u8 bDataCounter;
__be16 wBaseAddrHi;
__be16 wBaseAddrLo;
} __attribute__((packed));
struct ti_read_data_bytes {
__u8 bCmdCode;
__u8 bModuleId;
__u8 bErrorCode;
__u8 bData[0];
} __attribute__((packed));
/* Interrupt struct */
struct ti_interrupt {
__u8 bICode;
__u8 bIInfo;
} __attribute__((packed));
/* Interrupt codes */
#define TI_GET_PORT_FROM_CODE(c) (((c) >> 4) - 3)
#define TI_GET_FUNC_FROM_CODE(c) ((c) & 0x0f)
#define TI_CODE_HARDWARE_ERROR 0xFF
#define TI_CODE_DATA_ERROR 0x03
#define TI_CODE_MODEM_STATUS 0x04
/* Download firmware max packet size */
#define TI_DOWNLOAD_MAX_PACKET_SIZE 64
/* Firmware image header */
struct ti_firmware_header {
__le16 wLength;
__u8 bCheckSum;
} __attribute__((packed));
/* UART addresses */
#define TI_UART1_BASE_ADDR 0xFFA0 /* UART 1 base address */
#define TI_UART2_BASE_ADDR 0xFFB0 /* UART 2 base address */
#define TI_UART_OFFSET_LCR 0x0002 /* UART MCR register offset */
#define TI_UART_OFFSET_MCR 0x0004 /* UART MCR register offset */
#endif /* _TI_3410_5052_H_ */

View file

@ -0,0 +1,125 @@
/*
* USB Serial "Simple" driver
*
* Copyright (C) 2001-2006,2008,2013 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2005 Arthur Huillet (ahuillet@users.sf.net)
* Copyright (C) 2005 Thomas Hergenhahn <thomas.hergenhahn@suse.de>
* Copyright (C) 2009 Outpost Embedded, LLC
* Copyright (C) 2010 Zilogic Systems <code@zilogic.com>
* Copyright (C) 2013 Wei Shuai <cpuwolf@gmail.com>
* Copyright (C) 2013 Linux Foundation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#define DEVICE_N(vendor, IDS, nport) \
static const struct usb_device_id vendor##_id_table[] = { \
IDS(), \
{ }, \
}; \
static struct usb_serial_driver vendor##_device = { \
.driver = { \
.owner = THIS_MODULE, \
.name = #vendor, \
}, \
.id_table = vendor##_id_table, \
.num_ports = nport, \
};
#define DEVICE(vendor, IDS) DEVICE_N(vendor, IDS, 1)
/* Medtronic CareLink USB driver */
#define CARELINK_IDS() \
{ USB_DEVICE(0x0a21, 0x8001) } /* MMT-7305WW */
DEVICE(carelink, CARELINK_IDS);
/* ZIO Motherboard USB driver */
#define ZIO_IDS() \
{ USB_DEVICE(0x1CBE, 0x0103) }
DEVICE(zio, ZIO_IDS);
/* Funsoft Serial USB driver */
#define FUNSOFT_IDS() \
{ USB_DEVICE(0x1404, 0xcddc) }
DEVICE(funsoft, FUNSOFT_IDS);
/* Infineon Flashloader driver */
#define FLASHLOADER_IDS() \
{ USB_DEVICE(0x8087, 0x0716) }
DEVICE(flashloader, FLASHLOADER_IDS);
/* ViVOpay USB Serial Driver */
#define VIVOPAY_IDS() \
{ USB_DEVICE(0x1d5f, 0x1004) } /* ViVOpay 8800 */
DEVICE(vivopay, VIVOPAY_IDS);
/* Motorola USB Phone driver */
#define MOTO_IDS() \
{ USB_DEVICE(0x05c6, 0x3197) }, /* unknown Motorola phone */ \
{ USB_DEVICE(0x0c44, 0x0022) }, /* unknown Mororola phone */ \
{ USB_DEVICE(0x22b8, 0x2a64) }, /* Motorola KRZR K1m */ \
{ USB_DEVICE(0x22b8, 0x2c84) }, /* Motorola VE240 phone */ \
{ USB_DEVICE(0x22b8, 0x2c64) } /* Motorola V950 phone */
DEVICE(moto_modem, MOTO_IDS);
/* Novatel Wireless GPS driver */
#define NOVATEL_IDS() \
{ USB_DEVICE(0x09d7, 0x0100) } /* NovAtel FlexPack GPS */
DEVICE_N(novatel_gps, NOVATEL_IDS, 3);
/* HP4x (48/49) Generic Serial driver */
#define HP4X_IDS() \
{ USB_DEVICE(0x03f0, 0x0121) }
DEVICE(hp4x, HP4X_IDS);
/* Suunto ANT+ USB Driver */
#define SUUNTO_IDS() \
{ USB_DEVICE(0x0fcf, 0x1008) }, \
{ USB_DEVICE(0x0fcf, 0x1009) } /* Dynastream ANT USB-m Stick */
DEVICE(suunto, SUUNTO_IDS);
/* Siemens USB/MPI adapter */
#define SIEMENS_IDS() \
{ USB_DEVICE(0x908, 0x0004) }
DEVICE(siemens_mpi, SIEMENS_IDS);
/* All of the above structures mushed into two lists */
static struct usb_serial_driver * const serial_drivers[] = {
&carelink_device,
&zio_device,
&funsoft_device,
&flashloader_device,
&vivopay_device,
&moto_modem_device,
&novatel_gps_device,
&hp4x_device,
&suunto_device,
&siemens_mpi_device,
NULL
};
static const struct usb_device_id id_table[] = {
CARELINK_IDS(),
ZIO_IDS(),
FUNSOFT_IDS(),
FLASHLOADER_IDS(),
VIVOPAY_IDS(),
MOTO_IDS(),
NOVATEL_IDS(),
HP4X_IDS(),
SUUNTO_IDS(),
SIEMENS_IDS(),
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,64 @@
/*
* Definitions for USB serial mobile broadband cards
*/
#ifndef __LINUX_USB_USB_WWAN
#define __LINUX_USB_USB_WWAN
extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on);
extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port);
extern void usb_wwan_close(struct usb_serial_port *port);
extern int usb_wwan_port_probe(struct usb_serial_port *port);
extern int usb_wwan_port_remove(struct usb_serial_port *port);
extern int usb_wwan_write_room(struct tty_struct *tty);
extern int usb_wwan_tiocmget(struct tty_struct *tty);
extern int usb_wwan_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
extern int usb_wwan_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
extern int usb_wwan_chars_in_buffer(struct tty_struct *tty);
#ifdef CONFIG_PM
extern int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message);
extern int usb_wwan_resume(struct usb_serial *serial);
#endif
/* per port private data */
#define N_IN_URB 4
#define N_OUT_URB 4
#define IN_BUFLEN 4096
#define OUT_BUFLEN 4096
struct usb_wwan_intf_private {
spinlock_t susp_lock;
unsigned int suspended:1;
int in_flight;
unsigned int open_ports;
int (*send_setup) (struct usb_serial_port *port);
void *private;
};
struct usb_wwan_port_private {
/* Input endpoints and buffer for this port */
struct urb *in_urbs[N_IN_URB];
u8 *in_buffer[N_IN_URB];
/* Output endpoints and buffer for this port */
struct urb *out_urbs[N_OUT_URB];
u8 *out_buffer[N_OUT_URB];
unsigned long out_busy; /* Bit vector of URBs in use */
struct usb_anchor delayed;
/* Settings for the port */
int rts_state; /* Handshaking pins (outputs) */
int dtr_state;
int cts_state; /* Handshaking pins (inputs) */
int dsr_state;
int dcd_state;
int ri_state;
unsigned long tx_start_time[N_OUT_URB];
};
#endif /* __LINUX_USB_USB_WWAN */

View file

@ -0,0 +1,79 @@
/*
* USB Debug cable driver
*
* Copyright (C) 2006 Greg Kroah-Hartman <greg@kroah.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#define USB_DEBUG_MAX_PACKET_SIZE 8
#define USB_DEBUG_BRK_SIZE 8
static char USB_DEBUG_BRK[USB_DEBUG_BRK_SIZE] = {
0x00,
0xff,
0x01,
0xfe,
0x00,
0xfe,
0x01,
0xff,
};
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x0525, 0x127a) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
/* This HW really does not support a serial break, so one will be
* emulated when ever the break state is set to true.
*/
static void usb_debug_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
if (!break_state)
return;
usb_serial_generic_write(tty, port, USB_DEBUG_BRK, USB_DEBUG_BRK_SIZE);
}
static void usb_debug_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
if (urb->actual_length == USB_DEBUG_BRK_SIZE &&
memcmp(urb->transfer_buffer, USB_DEBUG_BRK,
USB_DEBUG_BRK_SIZE) == 0) {
usb_serial_handle_break(port);
return;
}
usb_serial_generic_process_read_urb(urb);
}
static struct usb_serial_driver debug_device = {
.driver = {
.owner = THIS_MODULE,
.name = "debug",
},
.id_table = id_table,
.num_ports = 1,
.bulk_out_size = USB_DEBUG_MAX_PACKET_SIZE,
.break_ctl = usb_debug_break_ctl,
.process_read_urb = usb_debug_process_read_urb,
};
static struct usb_serial_driver * const serial_drivers[] = {
&debug_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,695 @@
/*
USB Driver layer for GSM modems
Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de>
This driver is free software; you can redistribute it and/or modify
it under the terms of Version 2 of the GNU General Public License as
published by the Free Software Foundation.
Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org>
History: see the git log.
Work sponsored by: Sigos GmbH, Germany <info@sigos.de>
This driver exists because the "normal" serial driver doesn't work too well
with GSM modems. Issues:
- data loss -- one single Receive URB is not nearly enough
- controlling the baud rate doesn't make sense
*/
#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
#define DRIVER_DESC "USB Driver for GSM modems"
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial.h>
#include "usb-wwan.h"
void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
{
struct usb_wwan_port_private *portdata;
struct usb_wwan_intf_private *intfdata;
intfdata = usb_get_serial_data(port->serial);
if (!intfdata->send_setup)
return;
portdata = usb_get_serial_port_data(port);
/* FIXME: locking */
portdata->rts_state = on;
portdata->dtr_state = on;
intfdata->send_setup(port);
}
EXPORT_SYMBOL(usb_wwan_dtr_rts);
int usb_wwan_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
unsigned int value;
struct usb_wwan_port_private *portdata;
portdata = usb_get_serial_port_data(port);
value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
((portdata->dtr_state) ? TIOCM_DTR : 0) |
((portdata->cts_state) ? TIOCM_CTS : 0) |
((portdata->dsr_state) ? TIOCM_DSR : 0) |
((portdata->dcd_state) ? TIOCM_CAR : 0) |
((portdata->ri_state) ? TIOCM_RNG : 0);
return value;
}
EXPORT_SYMBOL(usb_wwan_tiocmget);
int usb_wwan_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_wwan_port_private *portdata;
struct usb_wwan_intf_private *intfdata;
portdata = usb_get_serial_port_data(port);
intfdata = usb_get_serial_data(port->serial);
if (!intfdata->send_setup)
return -EINVAL;
/* FIXME: what locks portdata fields ? */
if (set & TIOCM_RTS)
portdata->rts_state = 1;
if (set & TIOCM_DTR)
portdata->dtr_state = 1;
if (clear & TIOCM_RTS)
portdata->rts_state = 0;
if (clear & TIOCM_DTR)
portdata->dtr_state = 0;
return intfdata->send_setup(port);
}
EXPORT_SYMBOL(usb_wwan_tiocmset);
static int get_serial_info(struct usb_serial_port *port,
struct serial_struct __user *retinfo)
{
struct serial_struct tmp;
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.line = port->minor;
tmp.port = port->port_number;
tmp.baud_base = tty_get_baud_rate(port->port.tty);
tmp.close_delay = port->port.close_delay / 10;
tmp.closing_wait = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
port->port.closing_wait / 10;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
}
static int set_serial_info(struct usb_serial_port *port,
struct serial_struct __user *newinfo)
{
struct serial_struct new_serial;
unsigned int closing_wait, close_delay;
int retval = 0;
if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
return -EFAULT;
close_delay = new_serial.close_delay * 10;
closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
mutex_lock(&port->port.mutex);
if (!capable(CAP_SYS_ADMIN)) {
if ((close_delay != port->port.close_delay) ||
(closing_wait != port->port.closing_wait))
retval = -EPERM;
else
retval = -EOPNOTSUPP;
} else {
port->port.close_delay = close_delay;
port->port.closing_wait = closing_wait;
}
mutex_unlock(&port->port.mutex);
return retval;
}
int usb_wwan_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
switch (cmd) {
case TIOCGSERIAL:
return get_serial_info(port,
(struct serial_struct __user *) arg);
case TIOCSSERIAL:
return set_serial_info(port,
(struct serial_struct __user *) arg);
default:
break;
}
dev_dbg(&port->dev, "%s arg not supported\n", __func__);
return -ENOIOCTLCMD;
}
EXPORT_SYMBOL(usb_wwan_ioctl);
int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
{
struct usb_wwan_port_private *portdata;
struct usb_wwan_intf_private *intfdata;
int i;
int left, todo;
struct urb *this_urb = NULL; /* spurious */
int err;
unsigned long flags;
portdata = usb_get_serial_port_data(port);
intfdata = usb_get_serial_data(port->serial);
dev_dbg(&port->dev, "%s: write (%d chars)\n", __func__, count);
i = 0;
left = count;
for (i = 0; left > 0 && i < N_OUT_URB; i++) {
todo = left;
if (todo > OUT_BUFLEN)
todo = OUT_BUFLEN;
this_urb = portdata->out_urbs[i];
if (test_and_set_bit(i, &portdata->out_busy)) {
if (time_before(jiffies,
portdata->tx_start_time[i] + 10 * HZ))
continue;
usb_unlink_urb(this_urb);
continue;
}
dev_dbg(&port->dev, "%s: endpoint %d buf %d\n", __func__,
usb_pipeendpoint(this_urb->pipe), i);
err = usb_autopm_get_interface_async(port->serial->interface);
if (err < 0) {
clear_bit(i, &portdata->out_busy);
break;
}
/* send the data */
memcpy(this_urb->transfer_buffer, buf, todo);
this_urb->transfer_buffer_length = todo;
spin_lock_irqsave(&intfdata->susp_lock, flags);
if (intfdata->suspended) {
usb_anchor_urb(this_urb, &portdata->delayed);
spin_unlock_irqrestore(&intfdata->susp_lock, flags);
} else {
intfdata->in_flight++;
spin_unlock_irqrestore(&intfdata->susp_lock, flags);
err = usb_submit_urb(this_urb, GFP_ATOMIC);
if (err) {
dev_err(&port->dev,
"%s: submit urb %d failed: %d\n",
__func__, i, err);
clear_bit(i, &portdata->out_busy);
spin_lock_irqsave(&intfdata->susp_lock, flags);
intfdata->in_flight--;
spin_unlock_irqrestore(&intfdata->susp_lock,
flags);
usb_autopm_put_interface_async(port->serial->interface);
break;
}
}
portdata->tx_start_time[i] = jiffies;
buf += todo;
left -= todo;
}
count -= left;
dev_dbg(&port->dev, "%s: wrote (did %d)\n", __func__, count);
return count;
}
EXPORT_SYMBOL(usb_wwan_write);
static void usb_wwan_indat_callback(struct urb *urb)
{
int err;
int endpoint;
struct usb_serial_port *port;
struct device *dev;
unsigned char *data = urb->transfer_buffer;
int status = urb->status;
endpoint = usb_pipeendpoint(urb->pipe);
port = urb->context;
dev = &port->dev;
if (status) {
dev_dbg(dev, "%s: nonzero status: %d on endpoint %02x.\n",
__func__, status, endpoint);
} else {
if (urb->actual_length) {
tty_insert_flip_string(&port->port, data,
urb->actual_length);
tty_flip_buffer_push(&port->port);
} else
dev_dbg(dev, "%s: empty read urb received\n", __func__);
}
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
if (err != -EPERM) {
dev_err(dev, "%s: resubmit read urb failed. (%d)\n",
__func__, err);
/* busy also in error unless we are killed */
usb_mark_last_busy(port->serial->dev);
}
} else {
usb_mark_last_busy(port->serial->dev);
}
}
static void usb_wwan_outdat_callback(struct urb *urb)
{
struct usb_serial_port *port;
struct usb_wwan_port_private *portdata;
struct usb_wwan_intf_private *intfdata;
int i;
port = urb->context;
intfdata = usb_get_serial_data(port->serial);
usb_serial_port_softint(port);
usb_autopm_put_interface_async(port->serial->interface);
portdata = usb_get_serial_port_data(port);
spin_lock(&intfdata->susp_lock);
intfdata->in_flight--;
spin_unlock(&intfdata->susp_lock);
for (i = 0; i < N_OUT_URB; ++i) {
if (portdata->out_urbs[i] == urb) {
smp_mb__before_atomic();
clear_bit(i, &portdata->out_busy);
break;
}
}
}
int usb_wwan_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_wwan_port_private *portdata;
int i;
int data_len = 0;
struct urb *this_urb;
portdata = usb_get_serial_port_data(port);
for (i = 0; i < N_OUT_URB; i++) {
this_urb = portdata->out_urbs[i];
if (this_urb && !test_bit(i, &portdata->out_busy))
data_len += OUT_BUFLEN;
}
dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
return data_len;
}
EXPORT_SYMBOL(usb_wwan_write_room);
int usb_wwan_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_wwan_port_private *portdata;
int i;
int data_len = 0;
struct urb *this_urb;
portdata = usb_get_serial_port_data(port);
for (i = 0; i < N_OUT_URB; i++) {
this_urb = portdata->out_urbs[i];
/* FIXME: This locking is insufficient as this_urb may
go unused during the test */
if (this_urb && test_bit(i, &portdata->out_busy))
data_len += this_urb->transfer_buffer_length;
}
dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
return data_len;
}
EXPORT_SYMBOL(usb_wwan_chars_in_buffer);
int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_wwan_port_private *portdata;
struct usb_wwan_intf_private *intfdata;
struct usb_serial *serial = port->serial;
int i, err;
struct urb *urb;
portdata = usb_get_serial_port_data(port);
intfdata = usb_get_serial_data(serial);
if (port->interrupt_in_urb) {
err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (err) {
dev_err(&port->dev, "%s: submit int urb failed: %d\n",
__func__, err);
}
}
/* Start reading from the IN endpoint */
for (i = 0; i < N_IN_URB; i++) {
urb = portdata->in_urbs[i];
if (!urb)
continue;
err = usb_submit_urb(urb, GFP_KERNEL);
if (err) {
dev_err(&port->dev,
"%s: submit read urb %d failed: %d\n",
__func__, i, err);
}
}
spin_lock_irq(&intfdata->susp_lock);
if (++intfdata->open_ports == 1)
serial->interface->needs_remote_wakeup = 1;
spin_unlock_irq(&intfdata->susp_lock);
/* this balances a get in the generic USB serial code */
usb_autopm_put_interface(serial->interface);
return 0;
}
EXPORT_SYMBOL(usb_wwan_open);
static void unbusy_queued_urb(struct urb *urb,
struct usb_wwan_port_private *portdata)
{
int i;
for (i = 0; i < N_OUT_URB; i++) {
if (urb == portdata->out_urbs[i]) {
clear_bit(i, &portdata->out_busy);
break;
}
}
}
void usb_wwan_close(struct usb_serial_port *port)
{
int i;
struct usb_serial *serial = port->serial;
struct usb_wwan_port_private *portdata;
struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
struct urb *urb;
portdata = usb_get_serial_port_data(port);
/*
* Need to take susp_lock to make sure port is not already being
* resumed, but no need to hold it due to ASYNC_INITIALIZED.
*/
spin_lock_irq(&intfdata->susp_lock);
if (--intfdata->open_ports == 0)
serial->interface->needs_remote_wakeup = 0;
spin_unlock_irq(&intfdata->susp_lock);
for (;;) {
urb = usb_get_from_anchor(&portdata->delayed);
if (!urb)
break;
unbusy_queued_urb(urb, portdata);
usb_autopm_put_interface_async(serial->interface);
}
for (i = 0; i < N_IN_URB; i++)
usb_kill_urb(portdata->in_urbs[i]);
for (i = 0; i < N_OUT_URB; i++)
usb_kill_urb(portdata->out_urbs[i]);
usb_kill_urb(port->interrupt_in_urb);
usb_autopm_get_interface_no_resume(serial->interface);
}
EXPORT_SYMBOL(usb_wwan_close);
static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
int endpoint,
int dir, void *ctx, char *buf, int len,
void (*callback) (struct urb *))
{
struct usb_serial *serial = port->serial;
struct urb *urb;
urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
if (!urb)
return NULL;
usb_fill_bulk_urb(urb, serial->dev,
usb_sndbulkpipe(serial->dev, endpoint) | dir,
buf, len, callback, ctx);
return urb;
}
int usb_wwan_port_probe(struct usb_serial_port *port)
{
struct usb_wwan_port_private *portdata;
struct urb *urb;
u8 *buffer;
int i;
if (!port->bulk_in_size || !port->bulk_out_size)
return -ENODEV;
portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
if (!portdata)
return -ENOMEM;
init_usb_anchor(&portdata->delayed);
for (i = 0; i < N_IN_URB; i++) {
buffer = (u8 *)__get_free_page(GFP_KERNEL);
if (!buffer)
goto bail_out_error;
portdata->in_buffer[i] = buffer;
urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress,
USB_DIR_IN, port,
buffer, IN_BUFLEN,
usb_wwan_indat_callback);
portdata->in_urbs[i] = urb;
}
for (i = 0; i < N_OUT_URB; i++) {
buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
if (!buffer)
goto bail_out_error2;
portdata->out_buffer[i] = buffer;
urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress,
USB_DIR_OUT, port,
buffer, OUT_BUFLEN,
usb_wwan_outdat_callback);
portdata->out_urbs[i] = urb;
}
usb_set_serial_port_data(port, portdata);
return 0;
bail_out_error2:
for (i = 0; i < N_OUT_URB; i++) {
usb_free_urb(portdata->out_urbs[i]);
kfree(portdata->out_buffer[i]);
}
bail_out_error:
for (i = 0; i < N_IN_URB; i++) {
usb_free_urb(portdata->in_urbs[i]);
free_page((unsigned long)portdata->in_buffer[i]);
}
kfree(portdata);
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(usb_wwan_port_probe);
int usb_wwan_port_remove(struct usb_serial_port *port)
{
int i;
struct usb_wwan_port_private *portdata;
portdata = usb_get_serial_port_data(port);
usb_set_serial_port_data(port, NULL);
for (i = 0; i < N_IN_URB; i++) {
usb_free_urb(portdata->in_urbs[i]);
free_page((unsigned long)portdata->in_buffer[i]);
}
for (i = 0; i < N_OUT_URB; i++) {
usb_free_urb(portdata->out_urbs[i]);
kfree(portdata->out_buffer[i]);
}
kfree(portdata);
return 0;
}
EXPORT_SYMBOL(usb_wwan_port_remove);
#ifdef CONFIG_PM
static void stop_urbs(struct usb_serial *serial)
{
int i, j;
struct usb_serial_port *port;
struct usb_wwan_port_private *portdata;
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
portdata = usb_get_serial_port_data(port);
if (!portdata)
continue;
for (j = 0; j < N_IN_URB; j++)
usb_kill_urb(portdata->in_urbs[j]);
for (j = 0; j < N_OUT_URB; j++)
usb_kill_urb(portdata->out_urbs[j]);
usb_kill_urb(port->interrupt_in_urb);
}
}
int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
{
struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
spin_lock_irq(&intfdata->susp_lock);
if (PMSG_IS_AUTO(message)) {
if (intfdata->in_flight) {
spin_unlock_irq(&intfdata->susp_lock);
return -EBUSY;
}
}
intfdata->suspended = 1;
spin_unlock_irq(&intfdata->susp_lock);
stop_urbs(serial);
return 0;
}
EXPORT_SYMBOL(usb_wwan_suspend);
/* Caller must hold susp_lock. */
static int usb_wwan_submit_delayed_urbs(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
struct usb_wwan_port_private *portdata;
struct urb *urb;
int err_count = 0;
int err;
portdata = usb_get_serial_port_data(port);
for (;;) {
urb = usb_get_from_anchor(&portdata->delayed);
if (!urb)
break;
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
dev_err(&port->dev, "%s: submit urb failed: %d\n",
__func__, err);
err_count++;
unbusy_queued_urb(urb, portdata);
usb_autopm_put_interface_async(serial->interface);
continue;
}
data->in_flight++;
}
if (err_count)
return -EIO;
return 0;
}
int usb_wwan_resume(struct usb_serial *serial)
{
int i, j;
struct usb_serial_port *port;
struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
struct usb_wwan_port_private *portdata;
struct urb *urb;
int err;
int err_count = 0;
spin_lock_irq(&intfdata->susp_lock);
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags))
continue;
portdata = usb_get_serial_port_data(port);
if (port->interrupt_in_urb) {
err = usb_submit_urb(port->interrupt_in_urb,
GFP_ATOMIC);
if (err) {
dev_err(&port->dev,
"%s: submit int urb failed: %d\n",
__func__, err);
err_count++;
}
}
err = usb_wwan_submit_delayed_urbs(port);
if (err)
err_count++;
for (j = 0; j < N_IN_URB; j++) {
urb = portdata->in_urbs[j];
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
dev_err(&port->dev,
"%s: submit read urb %d failed: %d\n",
__func__, i, err);
err_count++;
}
}
}
intfdata->suspended = 0;
spin_unlock_irq(&intfdata->susp_lock);
if (err_count)
return -EIO;
return 0;
}
EXPORT_SYMBOL(usb_wwan_resume);
#endif
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

619
drivers/usb/serial/visor.c Normal file
View file

@ -0,0 +1,619 @@
/*
* USB HandSpring Visor, Palm m50x, and Sony Clie driver
* (supports all of the Palm OS USB devices)
*
* Copyright (C) 1999 - 2004
* Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/usb/cdc.h>
#include "visor.h"
/*
* Version Information
*/
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
#define DRIVER_DESC "USB HandSpring Visor / Palm OS driver"
/* function prototypes for a handspring visor */
static int visor_open(struct tty_struct *tty, struct usb_serial_port *port);
static void visor_close(struct usb_serial_port *port);
static int visor_probe(struct usb_serial *serial,
const struct usb_device_id *id);
static int visor_calc_num_ports(struct usb_serial *serial);
static void visor_read_int_callback(struct urb *urb);
static int clie_3_5_startup(struct usb_serial *serial);
static int treo_attach(struct usb_serial *serial);
static int clie_5_attach(struct usb_serial *serial);
static int palm_os_3_probe(struct usb_serial *serial,
const struct usb_device_id *id);
static int palm_os_4_probe(struct usb_serial *serial,
const struct usb_device_id *id);
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID),
.driver_info = (kernel_ulong_t)&palm_os_3_probe },
{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(GSPDA_VENDOR_ID, GSPDA_XPLORE_M68_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_TREO_650),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(ACER_VENDOR_ID, ACER_S10_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(TAPWAVE_VENDOR_ID, TAPWAVE_ZODIAC_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(FOSSIL_VENDOR_ID, FOSSIL_ABACUS_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ } /* Terminating entry */
};
static const struct usb_device_id clie_id_5_table[] = {
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ } /* Terminating entry */
};
static const struct usb_device_id clie_id_3_5_table[] = {
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) },
{ } /* Terminating entry */
};
static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) },
{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID) },
{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID) },
{ USB_DEVICE(GSPDA_VENDOR_ID, GSPDA_XPLORE_M68_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_TREO_650) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID) },
{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID) },
{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID) },
{ USB_DEVICE(TAPWAVE_VENDOR_ID, TAPWAVE_ZODIAC_ID) },
{ USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID) },
{ USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID) },
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID) },
{ USB_DEVICE(FOSSIL_VENDOR_ID, FOSSIL_ABACUS_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table_combined);
/* All of the device info needed for the Handspring Visor,
and Palm 4.0 devices */
static struct usb_serial_driver handspring_device = {
.driver = {
.owner = THIS_MODULE,
.name = "visor",
},
.description = "Handspring Visor / Palm OS",
.id_table = id_table,
.num_ports = 2,
.bulk_out_size = 256,
.open = visor_open,
.close = visor_close,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
.attach = treo_attach,
.probe = visor_probe,
.calc_num_ports = visor_calc_num_ports,
.read_int_callback = visor_read_int_callback,
};
/* All of the device info needed for the Clie UX50, TH55 Palm 5.0 devices */
static struct usb_serial_driver clie_5_device = {
.driver = {
.owner = THIS_MODULE,
.name = "clie_5",
},
.description = "Sony Clie 5.0",
.id_table = clie_id_5_table,
.num_ports = 2,
.bulk_out_size = 256,
.open = visor_open,
.close = visor_close,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
.attach = clie_5_attach,
.probe = visor_probe,
.calc_num_ports = visor_calc_num_ports,
.read_int_callback = visor_read_int_callback,
};
/* device info for the Sony Clie OS version 3.5 */
static struct usb_serial_driver clie_3_5_device = {
.driver = {
.owner = THIS_MODULE,
.name = "clie_3.5",
},
.description = "Sony Clie 3.5",
.id_table = clie_id_3_5_table,
.num_ports = 1,
.bulk_out_size = 256,
.open = visor_open,
.close = visor_close,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
.attach = clie_3_5_startup,
};
static struct usb_serial_driver * const serial_drivers[] = {
&handspring_device, &clie_5_device, &clie_3_5_device, NULL
};
/******************************************************************************
* Handspring Visor specific driver functions
******************************************************************************/
static int visor_open(struct tty_struct *tty, struct usb_serial_port *port)
{
int result = 0;
if (!port->read_urb) {
/* this is needed for some brain dead Sony devices */
dev_err(&port->dev, "Device lied about number of ports, please use a lower one.\n");
return -ENODEV;
}
/* Start reading from the device */
result = usb_serial_generic_open(tty, port);
if (result)
goto exit;
if (port->interrupt_in_urb) {
dev_dbg(&port->dev, "adding interrupt input for treo\n");
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result)
dev_err(&port->dev,
"%s - failed submitting interrupt urb, error %d\n",
__func__, result);
}
exit:
return result;
}
static void visor_close(struct usb_serial_port *port)
{
unsigned char *transfer_buffer;
usb_serial_generic_close(port);
usb_kill_urb(port->interrupt_in_urb);
transfer_buffer = kmalloc(0x12, GFP_KERNEL);
if (!transfer_buffer)
return;
usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
VISOR_CLOSE_NOTIFICATION, 0xc2,
0x0000, 0x0000,
transfer_buffer, 0x12, 300);
kfree(transfer_buffer);
}
static void visor_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
int status = urb->status;
int result;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
__func__, status);
goto exit;
}
/*
* This information is still unknown what it can be used for.
* If anyone has an idea, please let the author know...
*
* Rumor has it this endpoint is used to notify when data
* is ready to be read from the bulk ones.
*/
usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
urb->transfer_buffer);
exit:
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
dev_err(&urb->dev->dev,
"%s - Error %d submitting interrupt urb\n",
__func__, result);
}
static int palm_os_3_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
struct device *dev = &serial->dev->dev;
struct visor_connection_info *connection_info;
unsigned char *transfer_buffer;
char *string;
int retval = 0;
int i;
int num_ports = 0;
transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL);
if (!transfer_buffer)
return -ENOMEM;
/* send a get connection info request */
retval = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
VISOR_GET_CONNECTION_INFORMATION,
0xc2, 0x0000, 0x0000, transfer_buffer,
sizeof(*connection_info), 300);
if (retval < 0) {
dev_err(dev, "%s - error %d getting connection information\n",
__func__, retval);
goto exit;
}
if (retval == sizeof(*connection_info)) {
connection_info = (struct visor_connection_info *)
transfer_buffer;
num_ports = le16_to_cpu(connection_info->num_ports);
for (i = 0; i < num_ports; ++i) {
switch (
connection_info->connections[i].port_function_id) {
case VISOR_FUNCTION_GENERIC:
string = "Generic";
break;
case VISOR_FUNCTION_DEBUGGER:
string = "Debugger";
break;
case VISOR_FUNCTION_HOTSYNC:
string = "HotSync";
break;
case VISOR_FUNCTION_CONSOLE:
string = "Console";
break;
case VISOR_FUNCTION_REMOTE_FILE_SYS:
string = "Remote File System";
break;
default:
string = "unknown";
break;
}
dev_info(dev, "%s: port %d, is for %s use\n",
serial->type->description,
connection_info->connections[i].port, string);
}
}
/*
* Handle devices that report invalid stuff here.
*/
if (num_ports == 0 || num_ports > 2) {
dev_warn(dev, "%s: No valid connect info available\n",
serial->type->description);
num_ports = 2;
}
dev_info(dev, "%s: Number of ports: %d\n", serial->type->description,
num_ports);
/*
* save off our num_ports info so that we can use it in the
* calc_num_ports callback
*/
usb_set_serial_data(serial, (void *)(long)num_ports);
/* ask for the number of bytes available, but ignore the
response as it is broken */
retval = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
VISOR_REQUEST_BYTES_AVAILABLE,
0xc2, 0x0000, 0x0005, transfer_buffer,
0x02, 300);
if (retval < 0)
dev_err(dev, "%s - error %d getting bytes available request\n",
__func__, retval);
retval = 0;
exit:
kfree(transfer_buffer);
return retval;
}
static int palm_os_4_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
struct device *dev = &serial->dev->dev;
struct palm_ext_connection_info *connection_info;
unsigned char *transfer_buffer;
int retval;
transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL);
if (!transfer_buffer)
return -ENOMEM;
retval = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
PALM_GET_EXT_CONNECTION_INFORMATION,
0xc2, 0x0000, 0x0000, transfer_buffer,
sizeof(*connection_info), 300);
if (retval < 0)
dev_err(dev, "%s - error %d getting connection info\n",
__func__, retval);
else
usb_serial_debug_data(dev, __func__, retval, transfer_buffer);
kfree(transfer_buffer);
return 0;
}
static int visor_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
int retval = 0;
int (*startup)(struct usb_serial *serial,
const struct usb_device_id *id);
/*
* some Samsung Android phones in modem mode have the same ID
* as SPH-I500, but they are ACM devices, so dont bind to them
*/
if (id->idVendor == SAMSUNG_VENDOR_ID &&
id->idProduct == SAMSUNG_SPH_I500_ID &&
serial->dev->descriptor.bDeviceClass == USB_CLASS_COMM &&
serial->dev->descriptor.bDeviceSubClass ==
USB_CDC_SUBCLASS_ACM)
return -ENODEV;
if (serial->dev->actconfig->desc.bConfigurationValue != 1) {
dev_err(&serial->dev->dev, "active config #%d != 1 ??\n",
serial->dev->actconfig->desc.bConfigurationValue);
return -ENODEV;
}
if (id->driver_info) {
startup = (void *)id->driver_info;
retval = startup(serial, id);
}
return retval;
}
static int visor_calc_num_ports(struct usb_serial *serial)
{
int num_ports = (int)(long)(usb_get_serial_data(serial));
if (num_ports)
usb_set_serial_data(serial, NULL);
return num_ports;
}
static int clie_3_5_startup(struct usb_serial *serial)
{
struct device *dev = &serial->dev->dev;
int result;
u8 *data;
data = kmalloc(1, GFP_KERNEL);
if (!data)
return -ENOMEM;
/*
* Note that PEG-300 series devices expect the following two calls.
*/
/* get the config number */
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
USB_REQ_GET_CONFIGURATION, USB_DIR_IN,
0, 0, data, 1, 3000);
if (result < 0) {
dev_err(dev, "%s: get config number failed: %d\n",
__func__, result);
goto out;
}
if (result != 1) {
dev_err(dev, "%s: get config number bad return length: %d\n",
__func__, result);
result = -EIO;
goto out;
}
/* get the interface number */
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
USB_REQ_GET_INTERFACE,
USB_DIR_IN | USB_RECIP_INTERFACE,
0, 0, data, 1, 3000);
if (result < 0) {
dev_err(dev, "%s: get interface number failed: %d\n",
__func__, result);
goto out;
}
if (result != 1) {
dev_err(dev,
"%s: get interface number bad return length: %d\n",
__func__, result);
result = -EIO;
goto out;
}
result = 0;
out:
kfree(data);
return result;
}
static int treo_attach(struct usb_serial *serial)
{
struct usb_serial_port *swap_port;
/* Only do this endpoint hack for the Handspring devices with
* interrupt in endpoints, which for now are the Treo devices. */
if (!((le16_to_cpu(serial->dev->descriptor.idVendor)
== HANDSPRING_VENDOR_ID) ||
(le16_to_cpu(serial->dev->descriptor.idVendor)
== KYOCERA_VENDOR_ID)) ||
(serial->num_interrupt_in == 0))
return 0;
/*
* It appears that Treos and Kyoceras want to use the
* 1st bulk in endpoint to communicate with the 2nd bulk out endpoint,
* so let's swap the 1st and 2nd bulk in and interrupt endpoints.
* Note that swapping the bulk out endpoints would break lots of
* apps that want to communicate on the second port.
*/
#define COPY_PORT(dest, src) \
do { \
int i; \
\
for (i = 0; i < ARRAY_SIZE(src->read_urbs); ++i) { \
dest->read_urbs[i] = src->read_urbs[i]; \
dest->read_urbs[i]->context = dest; \
dest->bulk_in_buffers[i] = src->bulk_in_buffers[i]; \
} \
dest->read_urb = src->read_urb; \
dest->bulk_in_endpointAddress = src->bulk_in_endpointAddress;\
dest->bulk_in_buffer = src->bulk_in_buffer; \
dest->bulk_in_size = src->bulk_in_size; \
dest->interrupt_in_urb = src->interrupt_in_urb; \
dest->interrupt_in_urb->context = dest; \
dest->interrupt_in_endpointAddress = \
src->interrupt_in_endpointAddress;\
dest->interrupt_in_buffer = src->interrupt_in_buffer; \
} while (0);
swap_port = kmalloc(sizeof(*swap_port), GFP_KERNEL);
if (!swap_port)
return -ENOMEM;
COPY_PORT(swap_port, serial->port[0]);
COPY_PORT(serial->port[0], serial->port[1]);
COPY_PORT(serial->port[1], swap_port);
kfree(swap_port);
return 0;
}
static int clie_5_attach(struct usb_serial *serial)
{
struct usb_serial_port *port;
unsigned int pipe;
int j;
/* TH55 registers 2 ports.
Communication in from the UX50/TH55 uses bulk_in_endpointAddress
from port 0. Communication out to the UX50/TH55 uses
bulk_out_endpointAddress from port 1
Lets do a quick and dirty mapping
*/
/* some sanity check */
if (serial->num_ports < 2)
return -1;
/* port 0 now uses the modified endpoint Address */
port = serial->port[0];
port->bulk_out_endpointAddress =
serial->port[1]->bulk_out_endpointAddress;
pipe = usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress);
for (j = 0; j < ARRAY_SIZE(port->write_urbs); ++j)
port->write_urbs[j]->pipe = pipe;
return 0;
}
module_usb_serial_driver(serial_drivers, id_table_combined);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

161
drivers/usb/serial/visor.h Normal file
View file

@ -0,0 +1,161 @@
/*
* USB HandSpring Visor driver
*
* Copyright (C) 1999 - 2003
* Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver.
*
*/
#ifndef __LINUX_USB_SERIAL_VISOR_H
#define __LINUX_USB_SERIAL_VISOR_H
#define HANDSPRING_VENDOR_ID 0x082d
#define HANDSPRING_VISOR_ID 0x0100
#define HANDSPRING_TREO_ID 0x0200
#define HANDSPRING_TREO600_ID 0x0300
#define PALM_VENDOR_ID 0x0830
#define PALM_M500_ID 0x0001
#define PALM_M505_ID 0x0002
#define PALM_M515_ID 0x0003
#define PALM_I705_ID 0x0020
#define PALM_M125_ID 0x0040
#define PALM_M130_ID 0x0050
#define PALM_TUNGSTEN_T_ID 0x0060
#define PALM_TREO_650 0x0061
#define PALM_TUNGSTEN_Z_ID 0x0031
#define PALM_ZIRE_ID 0x0070
#define PALM_M100_ID 0x0080
#define GSPDA_VENDOR_ID 0x115e
#define GSPDA_XPLORE_M68_ID 0xf100
#define SONY_VENDOR_ID 0x054C
#define SONY_CLIE_3_5_ID 0x0038
#define SONY_CLIE_4_0_ID 0x0066
#define SONY_CLIE_S360_ID 0x0095
#define SONY_CLIE_4_1_ID 0x009A
#define SONY_CLIE_NX60_ID 0x00DA
#define SONY_CLIE_NZ90V_ID 0x00E9
#define SONY_CLIE_UX50_ID 0x0144
#define SONY_CLIE_TJ25_ID 0x0169
#define ACER_VENDOR_ID 0x0502
#define ACER_S10_ID 0x0001
#define SAMSUNG_VENDOR_ID 0x04E8
#define SAMSUNG_SCH_I330_ID 0x8001
#define SAMSUNG_SPH_I500_ID 0x6601
#define TAPWAVE_VENDOR_ID 0x12EF
#define TAPWAVE_ZODIAC_ID 0x0100
#define GARMIN_VENDOR_ID 0x091E
#define GARMIN_IQUE_3600_ID 0x0004
#define ACEECA_VENDOR_ID 0x4766
#define ACEECA_MEZ1000_ID 0x0001
#define KYOCERA_VENDOR_ID 0x0C88
#define KYOCERA_7135_ID 0x0021
#define FOSSIL_VENDOR_ID 0x0E67
#define FOSSIL_ABACUS_ID 0x0002
/****************************************************************************
* Handspring Visor Vendor specific request codes (bRequest values)
* A big thank you to Handspring for providing the following information.
* If anyone wants the original file where these values and structures came
* from, send email to <greg@kroah.com>.
****************************************************************************/
/****************************************************************************
* VISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that
* are available to be transferred to the host for the specified endpoint.
* Currently this is not used, and always returns 0x0001
****************************************************************************/
#define VISOR_REQUEST_BYTES_AVAILABLE 0x01
/****************************************************************************
* VISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host
* is now closing the pipe. An empty packet is sent in response.
****************************************************************************/
#define VISOR_CLOSE_NOTIFICATION 0x02
/****************************************************************************
* VISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to
* get the endpoints used by the connection.
****************************************************************************/
#define VISOR_GET_CONNECTION_INFORMATION 0x03
/****************************************************************************
* VISOR_GET_CONNECTION_INFORMATION returns data in the following format
****************************************************************************/
struct visor_connection_info {
__le16 num_ports;
struct {
__u8 port_function_id;
__u8 port;
} connections[2];
};
/* struct visor_connection_info.connection[x].port defines: */
#define VISOR_ENDPOINT_1 0x01
#define VISOR_ENDPOINT_2 0x02
/* struct visor_connection_info.connection[x].port_function_id defines: */
#define VISOR_FUNCTION_GENERIC 0x00
#define VISOR_FUNCTION_DEBUGGER 0x01
#define VISOR_FUNCTION_HOTSYNC 0x02
#define VISOR_FUNCTION_CONSOLE 0x03
#define VISOR_FUNCTION_REMOTE_FILE_SYS 0x04
/****************************************************************************
* PALM_GET_SOME_UNKNOWN_INFORMATION is sent by the host during enumeration to
* get some information from the M series devices, that is currently unknown.
****************************************************************************/
#define PALM_GET_EXT_CONNECTION_INFORMATION 0x04
/**
* struct palm_ext_connection_info - return data from a PALM_GET_EXT_CONNECTION_INFORMATION request
* @num_ports: maximum number of functions/connections in use
* @endpoint_numbers_different: will be 1 if in and out endpoints numbers are
* different, otherwise it is 0. If value is 1, then
* connections.end_point_info is non-zero. If value is 0, then
* connections.port contains the endpoint number, which is the same for in
* and out.
* @port_function_id: contains the creator id of the application that opened
* this connection.
* @port: contains the in/out endpoint number. Is 0 if in and out endpoint
* numbers are different.
* @end_point_info: high nubbe is in endpoint and low nibble will indicate out
* endpoint. Is 0 if in and out endpoints are the same.
*
* The maximum number of connections currently supported is 2
*/
struct palm_ext_connection_info {
__u8 num_ports;
__u8 endpoint_numbers_different;
__le16 reserved1;
struct {
__u32 port_function_id;
__u8 port;
__u8 end_point_info;
__le16 reserved;
} connections[2];
};
#endif

View file

@ -0,0 +1,840 @@
/*
* USB ConnectTech WhiteHEAT driver
*
* Copyright (C) 2002
* Connect Tech Inc.
*
* Copyright (C) 1999 - 2001
* Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <asm/termbits.h>
#include <linux/usb.h>
#include <linux/serial_reg.h>
#include <linux/serial.h>
#include <linux/usb/serial.h>
#include <linux/usb/ezusb.h>
#include "whiteheat.h" /* WhiteHEAT specific commands */
#ifndef CMSPAR
#define CMSPAR 0
#endif
/*
* Version Information
*/
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Stuart MacDonald <stuartm@connecttech.com>"
#define DRIVER_DESC "USB ConnectTech WhiteHEAT driver"
#define CONNECT_TECH_VENDOR_ID 0x0710
#define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001
#define CONNECT_TECH_WHITE_HEAT_ID 0x8001
/*
ID tables for whiteheat are unusual, because we want to different
things for different versions of the device. Eventually, this
will be doable from a single table. But, for now, we define two
separate ID tables, and then a third table that combines them
just for the purpose of exporting the autoloading information.
*/
static const struct usb_device_id id_table_std[] = {
{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
{ } /* Terminating entry */
};
static const struct usb_device_id id_table_prerenumeration[] = {
{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
{ } /* Terminating entry */
};
static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table_combined);
/* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */
static int whiteheat_firmware_download(struct usb_serial *serial,
const struct usb_device_id *id);
static int whiteheat_firmware_attach(struct usb_serial *serial);
/* function prototypes for the Connect Tech WhiteHEAT serial converter */
static int whiteheat_attach(struct usb_serial *serial);
static void whiteheat_release(struct usb_serial *serial);
static int whiteheat_port_probe(struct usb_serial_port *port);
static int whiteheat_port_remove(struct usb_serial_port *port);
static int whiteheat_open(struct tty_struct *tty,
struct usb_serial_port *port);
static void whiteheat_close(struct usb_serial_port *port);
static int whiteheat_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
static void whiteheat_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old);
static int whiteheat_tiocmget(struct tty_struct *tty);
static int whiteheat_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
static void whiteheat_break_ctl(struct tty_struct *tty, int break_state);
static struct usb_serial_driver whiteheat_fake_device = {
.driver = {
.owner = THIS_MODULE,
.name = "whiteheatnofirm",
},
.description = "Connect Tech - WhiteHEAT - (prerenumeration)",
.id_table = id_table_prerenumeration,
.num_ports = 1,
.probe = whiteheat_firmware_download,
.attach = whiteheat_firmware_attach,
};
static struct usb_serial_driver whiteheat_device = {
.driver = {
.owner = THIS_MODULE,
.name = "whiteheat",
},
.description = "Connect Tech - WhiteHEAT",
.id_table = id_table_std,
.num_ports = 4,
.attach = whiteheat_attach,
.release = whiteheat_release,
.port_probe = whiteheat_port_probe,
.port_remove = whiteheat_port_remove,
.open = whiteheat_open,
.close = whiteheat_close,
.ioctl = whiteheat_ioctl,
.set_termios = whiteheat_set_termios,
.break_ctl = whiteheat_break_ctl,
.tiocmget = whiteheat_tiocmget,
.tiocmset = whiteheat_tiocmset,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
};
static struct usb_serial_driver * const serial_drivers[] = {
&whiteheat_fake_device, &whiteheat_device, NULL
};
struct whiteheat_command_private {
struct mutex mutex;
__u8 port_running;
__u8 command_finished;
wait_queue_head_t wait_command; /* for handling sleeping whilst
waiting for a command to
finish */
__u8 result_buffer[64];
};
struct whiteheat_private {
__u8 mcr; /* FIXME: no locking on mcr */
};
/* local function prototypes */
static int start_command_port(struct usb_serial *serial);
static void stop_command_port(struct usb_serial *serial);
static void command_port_write_callback(struct urb *urb);
static void command_port_read_callback(struct urb *urb);
static int firm_send_command(struct usb_serial_port *port, __u8 command,
__u8 *data, __u8 datasize);
static int firm_open(struct usb_serial_port *port);
static int firm_close(struct usb_serial_port *port);
static void firm_setup_port(struct tty_struct *tty);
static int firm_set_rts(struct usb_serial_port *port, __u8 onoff);
static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff);
static int firm_set_break(struct usb_serial_port *port, __u8 onoff);
static int firm_purge(struct usb_serial_port *port, __u8 rxtx);
static int firm_get_dtr_rts(struct usb_serial_port *port);
static int firm_report_tx_done(struct usb_serial_port *port);
#define COMMAND_PORT 4
#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */
#define COMMAND_TIMEOUT_MS 2000
#define CLOSING_DELAY (30 * HZ)
/*****************************************************************************
* Connect Tech's White Heat prerenumeration driver functions
*****************************************************************************/
/* steps to download the firmware to the WhiteHEAT device:
- hold the reset (by writing to the reset bit of the CPUCS register)
- download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD
- release the reset (by writing to the CPUCS register)
- download the WH.HEX file for all addresses greater than 0x1b3f using
VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
- hold the reset
- download the WH.HEX file for all addresses less than 0x1b40 using
VENDOR_REQUEST_ANCHOR_LOAD
- release the reset
- device renumerated itself and comes up as new device id with all
firmware download completed.
*/
static int whiteheat_firmware_download(struct usb_serial *serial,
const struct usb_device_id *id)
{
int response;
response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat_loader.fw");
if (response >= 0) {
response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat.fw");
if (response >= 0)
return 0;
}
return -ENOENT;
}
static int whiteheat_firmware_attach(struct usb_serial *serial)
{
/* We want this device to fail to have a driver assigned to it */
return 1;
}
/*****************************************************************************
* Connect Tech's White Heat serial driver functions
*****************************************************************************/
static int whiteheat_attach(struct usb_serial *serial)
{
struct usb_serial_port *command_port;
struct whiteheat_command_private *command_info;
struct whiteheat_hw_info *hw_info;
int pipe;
int ret;
int alen;
__u8 *command;
__u8 *result;
command_port = serial->port[COMMAND_PORT];
pipe = usb_sndbulkpipe(serial->dev,
command_port->bulk_out_endpointAddress);
command = kmalloc(2, GFP_KERNEL);
if (!command)
goto no_command_buffer;
command[0] = WHITEHEAT_GET_HW_INFO;
command[1] = 0;
result = kmalloc(sizeof(*hw_info) + 1, GFP_KERNEL);
if (!result)
goto no_result_buffer;
/*
* When the module is reloaded the firmware is still there and
* the endpoints are still in the usb core unchanged. This is the
* unlinking bug in disguise. Same for the call below.
*/
usb_clear_halt(serial->dev, pipe);
ret = usb_bulk_msg(serial->dev, pipe, command, 2,
&alen, COMMAND_TIMEOUT_MS);
if (ret) {
dev_err(&serial->dev->dev, "%s: Couldn't send command [%d]\n",
serial->type->description, ret);
goto no_firmware;
} else if (alen != 2) {
dev_err(&serial->dev->dev, "%s: Send command incomplete [%d]\n",
serial->type->description, alen);
goto no_firmware;
}
pipe = usb_rcvbulkpipe(serial->dev,
command_port->bulk_in_endpointAddress);
/* See the comment on the usb_clear_halt() above */
usb_clear_halt(serial->dev, pipe);
ret = usb_bulk_msg(serial->dev, pipe, result,
sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS);
if (ret) {
dev_err(&serial->dev->dev, "%s: Couldn't get results [%d]\n",
serial->type->description, ret);
goto no_firmware;
} else if (alen != sizeof(*hw_info) + 1) {
dev_err(&serial->dev->dev, "%s: Get results incomplete [%d]\n",
serial->type->description, alen);
goto no_firmware;
} else if (result[0] != command[0]) {
dev_err(&serial->dev->dev, "%s: Command failed [%d]\n",
serial->type->description, result[0]);
goto no_firmware;
}
hw_info = (struct whiteheat_hw_info *)&result[1];
dev_info(&serial->dev->dev, "%s: Firmware v%d.%02d\n",
serial->type->description,
hw_info->sw_major_rev, hw_info->sw_minor_rev);
command_info = kmalloc(sizeof(struct whiteheat_command_private),
GFP_KERNEL);
if (!command_info)
goto no_command_private;
mutex_init(&command_info->mutex);
command_info->port_running = 0;
init_waitqueue_head(&command_info->wait_command);
usb_set_serial_port_data(command_port, command_info);
command_port->write_urb->complete = command_port_write_callback;
command_port->read_urb->complete = command_port_read_callback;
kfree(result);
kfree(command);
return 0;
no_firmware:
/* Firmware likely not running */
dev_err(&serial->dev->dev,
"%s: Unable to retrieve firmware version, try replugging\n",
serial->type->description);
dev_err(&serial->dev->dev,
"%s: If the firmware is not running (status led not blinking)\n",
serial->type->description);
dev_err(&serial->dev->dev,
"%s: please contact support@connecttech.com\n",
serial->type->description);
kfree(result);
kfree(command);
return -ENODEV;
no_command_private:
kfree(result);
no_result_buffer:
kfree(command);
no_command_buffer:
return -ENOMEM;
}
static void whiteheat_release(struct usb_serial *serial)
{
struct usb_serial_port *command_port;
/* free up our private data for our command port */
command_port = serial->port[COMMAND_PORT];
kfree(usb_get_serial_port_data(command_port));
}
static int whiteheat_port_probe(struct usb_serial_port *port)
{
struct whiteheat_private *info;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
usb_set_serial_port_data(port, info);
return 0;
}
static int whiteheat_port_remove(struct usb_serial_port *port)
{
struct whiteheat_private *info;
info = usb_get_serial_port_data(port);
kfree(info);
return 0;
}
static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port)
{
int retval;
retval = start_command_port(port->serial);
if (retval)
goto exit;
/* send an open port command */
retval = firm_open(port);
if (retval) {
stop_command_port(port->serial);
goto exit;
}
retval = firm_purge(port, WHITEHEAT_PURGE_RX | WHITEHEAT_PURGE_TX);
if (retval) {
firm_close(port);
stop_command_port(port->serial);
goto exit;
}
if (tty)
firm_setup_port(tty);
/* Work around HCD bugs */
usb_clear_halt(port->serial->dev, port->read_urb->pipe);
usb_clear_halt(port->serial->dev, port->write_urb->pipe);
retval = usb_serial_generic_open(tty, port);
if (retval) {
firm_close(port);
stop_command_port(port->serial);
goto exit;
}
exit:
return retval;
}
static void whiteheat_close(struct usb_serial_port *port)
{
firm_report_tx_done(port);
firm_close(port);
usb_serial_generic_close(port);
stop_command_port(port->serial);
}
static int whiteheat_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct whiteheat_private *info = usb_get_serial_port_data(port);
unsigned int modem_signals = 0;
firm_get_dtr_rts(port);
if (info->mcr & UART_MCR_DTR)
modem_signals |= TIOCM_DTR;
if (info->mcr & UART_MCR_RTS)
modem_signals |= TIOCM_RTS;
return modem_signals;
}
static int whiteheat_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
struct whiteheat_private *info = usb_get_serial_port_data(port);
if (set & TIOCM_RTS)
info->mcr |= UART_MCR_RTS;
if (set & TIOCM_DTR)
info->mcr |= UART_MCR_DTR;
if (clear & TIOCM_RTS)
info->mcr &= ~UART_MCR_RTS;
if (clear & TIOCM_DTR)
info->mcr &= ~UART_MCR_DTR;
firm_set_dtr(port, info->mcr & UART_MCR_DTR);
firm_set_rts(port, info->mcr & UART_MCR_RTS);
return 0;
}
static int whiteheat_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
struct serial_struct serstruct;
void __user *user_arg = (void __user *)arg;
switch (cmd) {
case TIOCGSERIAL:
memset(&serstruct, 0, sizeof(serstruct));
serstruct.type = PORT_16654;
serstruct.line = port->minor;
serstruct.port = port->port_number;
serstruct.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
serstruct.xmit_fifo_size = kfifo_size(&port->write_fifo);
serstruct.custom_divisor = 0;
serstruct.baud_base = 460800;
serstruct.close_delay = CLOSING_DELAY;
serstruct.closing_wait = CLOSING_DELAY;
if (copy_to_user(user_arg, &serstruct, sizeof(serstruct)))
return -EFAULT;
break;
default:
break;
}
return -ENOIOCTLCMD;
}
static void whiteheat_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
firm_setup_port(tty);
}
static void whiteheat_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
firm_set_break(port, break_state);
}
/*****************************************************************************
* Connect Tech's White Heat callback routines
*****************************************************************************/
static void command_port_write_callback(struct urb *urb)
{
int status = urb->status;
if (status) {
dev_dbg(&urb->dev->dev, "nonzero urb status: %d\n", status);
return;
}
}
static void command_port_read_callback(struct urb *urb)
{
struct usb_serial_port *command_port = urb->context;
struct whiteheat_command_private *command_info;
int status = urb->status;
unsigned char *data = urb->transfer_buffer;
int result;
command_info = usb_get_serial_port_data(command_port);
if (!command_info) {
dev_dbg(&urb->dev->dev, "%s - command_info is NULL, exiting.\n", __func__);
return;
}
if (!urb->actual_length) {
dev_dbg(&urb->dev->dev, "%s - empty response, exiting.\n", __func__);
return;
}
if (status) {
dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, status);
if (status != -ENOENT)
command_info->command_finished = WHITEHEAT_CMD_FAILURE;
wake_up(&command_info->wait_command);
return;
}
usb_serial_debug_data(&command_port->dev, __func__, urb->actual_length, data);
if (data[0] == WHITEHEAT_CMD_COMPLETE) {
command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
wake_up(&command_info->wait_command);
} else if (data[0] == WHITEHEAT_CMD_FAILURE) {
command_info->command_finished = WHITEHEAT_CMD_FAILURE;
wake_up(&command_info->wait_command);
} else if (data[0] == WHITEHEAT_EVENT) {
/* These are unsolicited reports from the firmware, hence no
waiting command to wakeup */
dev_dbg(&urb->dev->dev, "%s - event received\n", __func__);
} else if ((data[0] == WHITEHEAT_GET_DTR_RTS) &&
(urb->actual_length - 1 <= sizeof(command_info->result_buffer))) {
memcpy(command_info->result_buffer, &data[1],
urb->actual_length - 1);
command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
wake_up(&command_info->wait_command);
} else
dev_dbg(&urb->dev->dev, "%s - bad reply from firmware\n", __func__);
/* Continue trying to always read */
result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
if (result)
dev_dbg(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n",
__func__, result);
}
/*****************************************************************************
* Connect Tech's White Heat firmware interface
*****************************************************************************/
static int firm_send_command(struct usb_serial_port *port, __u8 command,
__u8 *data, __u8 datasize)
{
struct usb_serial_port *command_port;
struct whiteheat_command_private *command_info;
struct whiteheat_private *info;
struct device *dev = &port->dev;
__u8 *transfer_buffer;
int retval = 0;
int t;
dev_dbg(dev, "%s - command %d\n", __func__, command);
command_port = port->serial->port[COMMAND_PORT];
command_info = usb_get_serial_port_data(command_port);
mutex_lock(&command_info->mutex);
command_info->command_finished = false;
transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
transfer_buffer[0] = command;
memcpy(&transfer_buffer[1], data, datasize);
command_port->write_urb->transfer_buffer_length = datasize + 1;
retval = usb_submit_urb(command_port->write_urb, GFP_NOIO);
if (retval) {
dev_dbg(dev, "%s - submit urb failed\n", __func__);
goto exit;
}
/* wait for the command to complete */
t = wait_event_timeout(command_info->wait_command,
(bool)command_info->command_finished, COMMAND_TIMEOUT);
if (!t)
usb_kill_urb(command_port->write_urb);
if (command_info->command_finished == false) {
dev_dbg(dev, "%s - command timed out.\n", __func__);
retval = -ETIMEDOUT;
goto exit;
}
if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) {
dev_dbg(dev, "%s - command failed.\n", __func__);
retval = -EIO;
goto exit;
}
if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) {
dev_dbg(dev, "%s - command completed.\n", __func__);
switch (command) {
case WHITEHEAT_GET_DTR_RTS:
info = usb_get_serial_port_data(port);
memcpy(&info->mcr, command_info->result_buffer,
sizeof(struct whiteheat_dr_info));
break;
}
}
exit:
mutex_unlock(&command_info->mutex);
return retval;
}
static int firm_open(struct usb_serial_port *port)
{
struct whiteheat_simple open_command;
open_command.port = port->port_number + 1;
return firm_send_command(port, WHITEHEAT_OPEN,
(__u8 *)&open_command, sizeof(open_command));
}
static int firm_close(struct usb_serial_port *port)
{
struct whiteheat_simple close_command;
close_command.port = port->port_number + 1;
return firm_send_command(port, WHITEHEAT_CLOSE,
(__u8 *)&close_command, sizeof(close_command));
}
static void firm_setup_port(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct device *dev = &port->dev;
struct whiteheat_port_settings port_settings;
unsigned int cflag = tty->termios.c_cflag;
port_settings.port = port->port_number + 1;
/* get the byte size */
switch (cflag & CSIZE) {
case CS5: port_settings.bits = 5; break;
case CS6: port_settings.bits = 6; break;
case CS7: port_settings.bits = 7; break;
default:
case CS8: port_settings.bits = 8; break;
}
dev_dbg(dev, "%s - data bits = %d\n", __func__, port_settings.bits);
/* determine the parity */
if (cflag & PARENB)
if (cflag & CMSPAR)
if (cflag & PARODD)
port_settings.parity = WHITEHEAT_PAR_MARK;
else
port_settings.parity = WHITEHEAT_PAR_SPACE;
else
if (cflag & PARODD)
port_settings.parity = WHITEHEAT_PAR_ODD;
else
port_settings.parity = WHITEHEAT_PAR_EVEN;
else
port_settings.parity = WHITEHEAT_PAR_NONE;
dev_dbg(dev, "%s - parity = %c\n", __func__, port_settings.parity);
/* figure out the stop bits requested */
if (cflag & CSTOPB)
port_settings.stop = 2;
else
port_settings.stop = 1;
dev_dbg(dev, "%s - stop bits = %d\n", __func__, port_settings.stop);
/* figure out the flow control settings */
if (cflag & CRTSCTS)
port_settings.hflow = (WHITEHEAT_HFLOW_CTS |
WHITEHEAT_HFLOW_RTS);
else
port_settings.hflow = WHITEHEAT_HFLOW_NONE;
dev_dbg(dev, "%s - hardware flow control = %s %s %s %s\n", __func__,
(port_settings.hflow & WHITEHEAT_HFLOW_CTS) ? "CTS" : "",
(port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "",
(port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "",
(port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : "");
/* determine software flow control */
if (I_IXOFF(tty))
port_settings.sflow = WHITEHEAT_SFLOW_RXTX;
else
port_settings.sflow = WHITEHEAT_SFLOW_NONE;
dev_dbg(dev, "%s - software flow control = %c\n", __func__, port_settings.sflow);
port_settings.xon = START_CHAR(tty);
port_settings.xoff = STOP_CHAR(tty);
dev_dbg(dev, "%s - XON = %2x, XOFF = %2x\n", __func__, port_settings.xon, port_settings.xoff);
/* get the baud rate wanted */
port_settings.baud = tty_get_baud_rate(tty);
dev_dbg(dev, "%s - baud rate = %d\n", __func__, port_settings.baud);
/* fixme: should set validated settings */
tty_encode_baud_rate(tty, port_settings.baud, port_settings.baud);
/* handle any settings that aren't specified in the tty structure */
port_settings.lloop = 0;
/* now send the message to the device */
firm_send_command(port, WHITEHEAT_SETUP_PORT,
(__u8 *)&port_settings, sizeof(port_settings));
}
static int firm_set_rts(struct usb_serial_port *port, __u8 onoff)
{
struct whiteheat_set_rdb rts_command;
rts_command.port = port->port_number + 1;
rts_command.state = onoff;
return firm_send_command(port, WHITEHEAT_SET_RTS,
(__u8 *)&rts_command, sizeof(rts_command));
}
static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff)
{
struct whiteheat_set_rdb dtr_command;
dtr_command.port = port->port_number + 1;
dtr_command.state = onoff;
return firm_send_command(port, WHITEHEAT_SET_DTR,
(__u8 *)&dtr_command, sizeof(dtr_command));
}
static int firm_set_break(struct usb_serial_port *port, __u8 onoff)
{
struct whiteheat_set_rdb break_command;
break_command.port = port->port_number + 1;
break_command.state = onoff;
return firm_send_command(port, WHITEHEAT_SET_BREAK,
(__u8 *)&break_command, sizeof(break_command));
}
static int firm_purge(struct usb_serial_port *port, __u8 rxtx)
{
struct whiteheat_purge purge_command;
purge_command.port = port->port_number + 1;
purge_command.what = rxtx;
return firm_send_command(port, WHITEHEAT_PURGE,
(__u8 *)&purge_command, sizeof(purge_command));
}
static int firm_get_dtr_rts(struct usb_serial_port *port)
{
struct whiteheat_simple get_dr_command;
get_dr_command.port = port->port_number + 1;
return firm_send_command(port, WHITEHEAT_GET_DTR_RTS,
(__u8 *)&get_dr_command, sizeof(get_dr_command));
}
static int firm_report_tx_done(struct usb_serial_port *port)
{
struct whiteheat_simple close_command;
close_command.port = port->port_number + 1;
return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE,
(__u8 *)&close_command, sizeof(close_command));
}
/*****************************************************************************
* Connect Tech's White Heat utility functions
*****************************************************************************/
static int start_command_port(struct usb_serial *serial)
{
struct usb_serial_port *command_port;
struct whiteheat_command_private *command_info;
int retval = 0;
command_port = serial->port[COMMAND_PORT];
command_info = usb_get_serial_port_data(command_port);
mutex_lock(&command_info->mutex);
if (!command_info->port_running) {
/* Work around HCD bugs */
usb_clear_halt(serial->dev, command_port->read_urb->pipe);
retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL);
if (retval) {
dev_err(&serial->dev->dev,
"%s - failed submitting read urb, error %d\n",
__func__, retval);
goto exit;
}
}
command_info->port_running++;
exit:
mutex_unlock(&command_info->mutex);
return retval;
}
static void stop_command_port(struct usb_serial *serial)
{
struct usb_serial_port *command_port;
struct whiteheat_command_private *command_info;
command_port = serial->port[COMMAND_PORT];
command_info = usb_get_serial_port_data(command_port);
mutex_lock(&command_info->mutex);
command_info->port_running--;
if (!command_info->port_running)
usb_kill_urb(command_port->read_urb);
mutex_unlock(&command_info->mutex);
}
module_usb_serial_driver(serial_drivers, id_table_combined);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("whiteheat.fw");
MODULE_FIRMWARE("whiteheat_loader.fw");

View file

@ -0,0 +1,302 @@
/*
* USB ConnectTech WhiteHEAT driver
*
* Copyright (C) 2002
* Connect Tech Inc.
*
* Copyright (C) 1999, 2000
* Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See Documentation/usb/usb-serial.txt for more information on using this
* driver
*
*/
#ifndef __LINUX_USB_SERIAL_WHITEHEAT_H
#define __LINUX_USB_SERIAL_WHITEHEAT_H
/* WhiteHEAT commands */
#define WHITEHEAT_OPEN 1 /* open the port */
#define WHITEHEAT_CLOSE 2 /* close the port */
#define WHITEHEAT_SETUP_PORT 3 /* change port settings */
#define WHITEHEAT_SET_RTS 4 /* turn RTS on or off */
#define WHITEHEAT_SET_DTR 5 /* turn DTR on or off */
#define WHITEHEAT_SET_BREAK 6 /* turn BREAK on or off */
#define WHITEHEAT_DUMP 7 /* dump memory */
#define WHITEHEAT_STATUS 8 /* get status */
#define WHITEHEAT_PURGE 9 /* clear the UART fifos */
#define WHITEHEAT_GET_DTR_RTS 10 /* get the state of DTR and RTS
for a port */
#define WHITEHEAT_GET_HW_INFO 11 /* get EEPROM info and
hardware ID */
#define WHITEHEAT_REPORT_TX_DONE 12 /* get the next TX done */
#define WHITEHEAT_EVENT 13 /* unsolicited status events */
#define WHITEHEAT_ECHO 14 /* send data to the indicated
IN endpoint */
#define WHITEHEAT_DO_TEST 15 /* perform specified test */
#define WHITEHEAT_CMD_COMPLETE 16 /* reply for some commands */
#define WHITEHEAT_CMD_FAILURE 17 /* reply for failed commands */
/*
* Commands to the firmware
*/
/*
* WHITEHEAT_OPEN
* WHITEHEAT_CLOSE
* WHITEHEAT_STATUS
* WHITEHEAT_GET_DTR_RTS
* WHITEHEAT_REPORT_TX_DONE
*/
struct whiteheat_simple {
__u8 port; /* port number (1 to N) */
};
/*
* WHITEHEAT_SETUP_PORT
*/
#define WHITEHEAT_PAR_NONE 'n' /* no parity */
#define WHITEHEAT_PAR_EVEN 'e' /* even parity */
#define WHITEHEAT_PAR_ODD 'o' /* odd parity */
#define WHITEHEAT_PAR_SPACE '0' /* space (force 0) parity */
#define WHITEHEAT_PAR_MARK '1' /* mark (force 1) parity */
#define WHITEHEAT_SFLOW_NONE 'n' /* no software flow control */
#define WHITEHEAT_SFLOW_RX 'r' /* XOFF/ON is sent when RX
fills/empties */
#define WHITEHEAT_SFLOW_TX 't' /* when received XOFF/ON will
stop/start TX */
#define WHITEHEAT_SFLOW_RXTX 'b' /* both SFLOW_RX and SFLOW_TX */
#define WHITEHEAT_HFLOW_NONE 0x00 /* no hardware flow control */
#define WHITEHEAT_HFLOW_RTS_TOGGLE 0x01 /* RTS is on during transmit,
off otherwise */
#define WHITEHEAT_HFLOW_DTR 0x02 /* DTR is off/on when RX
fills/empties */
#define WHITEHEAT_HFLOW_CTS 0x08 /* when received CTS off/on
will stop/start TX */
#define WHITEHEAT_HFLOW_DSR 0x10 /* when received DSR off/on
will stop/start TX */
#define WHITEHEAT_HFLOW_RTS 0x80 /* RTS is off/on when RX
fills/empties */
struct whiteheat_port_settings {
__u8 port; /* port number (1 to N) */
__u32 baud; /* any value 7 - 460800, firmware calculates
best fit; arrives little endian */
__u8 bits; /* 5, 6, 7, or 8 */
__u8 stop; /* 1 or 2, default 1 (2 = 1.5 if bits = 5) */
__u8 parity; /* see WHITEHEAT_PAR_* above */
__u8 sflow; /* see WHITEHEAT_SFLOW_* above */
__u8 xoff; /* XOFF byte value */
__u8 xon; /* XON byte value */
__u8 hflow; /* see WHITEHEAT_HFLOW_* above */
__u8 lloop; /* 0/1 turns local loopback mode off/on */
} __attribute__ ((packed));
/*
* WHITEHEAT_SET_RTS
* WHITEHEAT_SET_DTR
* WHITEHEAT_SET_BREAK
*/
#define WHITEHEAT_RTS_OFF 0x00
#define WHITEHEAT_RTS_ON 0x01
#define WHITEHEAT_DTR_OFF 0x00
#define WHITEHEAT_DTR_ON 0x01
#define WHITEHEAT_BREAK_OFF 0x00
#define WHITEHEAT_BREAK_ON 0x01
struct whiteheat_set_rdb {
__u8 port; /* port number (1 to N) */
__u8 state; /* 0/1 turns signal off/on */
};
/*
* WHITEHEAT_DUMP
*/
#define WHITEHEAT_DUMP_MEM_DATA 'd' /* data */
#define WHITEHEAT_DUMP_MEM_IDATA 'i' /* idata */
#define WHITEHEAT_DUMP_MEM_BDATA 'b' /* bdata */
#define WHITEHEAT_DUMP_MEM_XDATA 'x' /* xdata */
/*
* Allowable address ranges (firmware checks address):
* Type DATA: 0x00 - 0xff
* Type IDATA: 0x80 - 0xff
* Type BDATA: 0x20 - 0x2f
* Type XDATA: 0x0000 - 0xffff
*
* B/I/DATA all read the local memory space
* XDATA reads the external memory space
* BDATA returns bits as bytes
*
* NOTE: 0x80 - 0xff (local space) are the Special Function Registers
* of the 8051, and some have on-read side-effects.
*/
struct whiteheat_dump {
__u8 mem_type; /* see WHITEHEAT_DUMP_* above */
__u16 addr; /* address, see restrictions above */
__u16 length; /* number of bytes to dump, max 63 bytes */
};
/*
* WHITEHEAT_PURGE
*/
#define WHITEHEAT_PURGE_RX 0x01 /* purge rx fifos */
#define WHITEHEAT_PURGE_TX 0x02 /* purge tx fifos */
struct whiteheat_purge {
__u8 port; /* port number (1 to N) */
__u8 what; /* bit pattern of what to purge */
};
/*
* WHITEHEAT_ECHO
*/
struct whiteheat_echo {
__u8 port; /* port number (1 to N) */
__u8 length; /* length of message to echo, max 61 bytes */
__u8 echo_data[61]; /* data to echo */
};
/*
* WHITEHEAT_DO_TEST
*/
#define WHITEHEAT_TEST_UART_RW 0x01 /* read/write uart registers */
#define WHITEHEAT_TEST_UART_INTR 0x02 /* uart interrupt */
#define WHITEHEAT_TEST_SETUP_CONT 0x03 /* setup for
PORT_CONT/PORT_DISCONT */
#define WHITEHEAT_TEST_PORT_CONT 0x04 /* port connect */
#define WHITEHEAT_TEST_PORT_DISCONT 0x05 /* port disconnect */
#define WHITEHEAT_TEST_UART_CLK_START 0x06 /* uart clock test start */
#define WHITEHEAT_TEST_UART_CLK_STOP 0x07 /* uart clock test stop */
#define WHITEHEAT_TEST_MODEM_FT 0x08 /* modem signals, requires a
loopback cable/connector */
#define WHITEHEAT_TEST_ERASE_EEPROM 0x09 /* erase eeprom */
#define WHITEHEAT_TEST_READ_EEPROM 0x0a /* read eeprom */
#define WHITEHEAT_TEST_PROGRAM_EEPROM 0x0b /* program eeprom */
struct whiteheat_test {
__u8 port; /* port number (1 to n) */
__u8 test; /* see WHITEHEAT_TEST_* above*/
__u8 info[32]; /* additional info */
};
/*
* Replies from the firmware
*/
/*
* WHITEHEAT_STATUS
*/
#define WHITEHEAT_EVENT_MODEM 0x01 /* modem field is valid */
#define WHITEHEAT_EVENT_ERROR 0x02 /* error field is valid */
#define WHITEHEAT_EVENT_FLOW 0x04 /* flow field is valid */
#define WHITEHEAT_EVENT_CONNECT 0x08 /* connect field is valid */
#define WHITEHEAT_FLOW_NONE 0x00 /* no flow control active */
#define WHITEHEAT_FLOW_HARD_OUT 0x01 /* TX is stopped by CTS
(waiting for CTS to go on) */
#define WHITEHEAT_FLOW_HARD_IN 0x02 /* remote TX is stopped
by RTS */
#define WHITEHEAT_FLOW_SOFT_OUT 0x04 /* TX is stopped by XOFF
received (waiting for XON) */
#define WHITEHEAT_FLOW_SOFT_IN 0x08 /* remote TX is stopped by XOFF
transmitted */
#define WHITEHEAT_FLOW_TX_DONE 0x80 /* TX has completed */
struct whiteheat_status_info {
__u8 port; /* port number (1 to N) */
__u8 event; /* indicates what the current event is,
see WHITEHEAT_EVENT_* above */
__u8 modem; /* modem signal status (copy of uart's
MSR register) */
__u8 error; /* line status (copy of uart's LSR register) */
__u8 flow; /* flow control state, see WHITEHEAT_FLOW_*
above */
__u8 connect; /* 0 means not connected, non-zero means
connected */
};
/*
* WHITEHEAT_GET_DTR_RTS
*/
struct whiteheat_dr_info {
__u8 mcr; /* copy of uart's MCR register */
};
/*
* WHITEHEAT_GET_HW_INFO
*/
struct whiteheat_hw_info {
__u8 hw_id; /* hardware id number, WhiteHEAT = 0 */
__u8 sw_major_rev; /* major version number */
__u8 sw_minor_rev; /* minor version number */
struct whiteheat_hw_eeprom_info {
__u8 b0; /* B0 */
__u8 vendor_id_low; /* vendor id (low byte) */
__u8 vendor_id_high; /* vendor id (high byte) */
__u8 product_id_low; /* product id (low byte) */
__u8 product_id_high; /* product id (high byte) */
__u8 device_id_low; /* device id (low byte) */
__u8 device_id_high; /* device id (high byte) */
__u8 not_used_1;
__u8 serial_number_0; /* serial number (low byte) */
__u8 serial_number_1; /* serial number */
__u8 serial_number_2; /* serial number */
__u8 serial_number_3; /* serial number (high byte) */
__u8 not_used_2;
__u8 not_used_3;
__u8 checksum_low; /* checksum (low byte) */
__u8 checksum_high; /* checksum (high byte */
} hw_eeprom_info; /* EEPROM contents */
};
/*
* WHITEHEAT_EVENT
*/
struct whiteheat_event_info {
__u8 port; /* port number (1 to N) */
__u8 event; /* see whiteheat_status_info.event */
__u8 info; /* see whiteheat_status_info.modem, .error,
.flow, .connect */
};
/*
* WHITEHEAT_DO_TEST
*/
#define WHITEHEAT_TEST_FAIL 0x00 /* test failed */
#define WHITEHEAT_TEST_UNKNOWN 0x01 /* unknown test requested */
#define WHITEHEAT_TEST_PASS 0xff /* test passed */
struct whiteheat_test_info {
__u8 port; /* port number (1 to N) */
__u8 test; /* indicates which test this is a response for,
see WHITEHEAT_DO_TEST above */
__u8 status; /* see WHITEHEAT_TEST_* above */
__u8 results[32]; /* test-dependent results */
};
#endif

View file

@ -0,0 +1,94 @@
/*
* USB Wishbone-Serial adapter driver
*
* Copyright (C) 2013 Wesley W. Terpstra <w.terpstra@gsi.de>
* Copyright (C) 2013 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
#define GSI_VENDOR_OPENCLOSE 0xB0
static const struct usb_device_id id_table[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x1D50, 0x6062, 0xFF, 0xFF, 0xFF) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
/*
* Etherbone must be told that a new stream has begun before data arrives.
* This is necessary to restart the negotiation of Wishbone bus parameters.
* Similarly, when the stream ends, Etherbone must be told so that the cycle
* line can be driven low in the case that userspace failed to do so.
*/
static int usb_gsi_openclose(struct usb_serial_port *port, int value)
{
struct usb_device *dev = port->serial->dev;
return usb_control_msg(
dev,
usb_sndctrlpipe(dev, 0), /* Send to EP0OUT */
GSI_VENDOR_OPENCLOSE,
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
value, /* wValue = device is open(1) or closed(0) */
port->serial->interface->cur_altsetting->desc.bInterfaceNumber,
NULL, 0, /* There is no data stage */
5000); /* Timeout till operation fails */
}
static int wishbone_serial_open(struct tty_struct *tty,
struct usb_serial_port *port)
{
int retval;
retval = usb_gsi_openclose(port, 1);
if (retval) {
dev_err(&port->serial->dev->dev,
"Could not mark device as open (%d)\n",
retval);
return retval;
}
retval = usb_serial_generic_open(tty, port);
if (retval)
usb_gsi_openclose(port, 0);
return retval;
}
static void wishbone_serial_close(struct usb_serial_port *port)
{
usb_serial_generic_close(port);
usb_gsi_openclose(port, 0);
}
static struct usb_serial_driver wishbone_serial_device = {
.driver = {
.owner = THIS_MODULE,
.name = "wishbone_serial",
},
.id_table = id_table,
.num_ports = 1,
.open = &wishbone_serial_open,
.close = &wishbone_serial_close,
};
static struct usb_serial_driver * const serial_drivers[] = {
&wishbone_serial_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR("Wesley W. Terpstra <w.terpstra@gsi.de>");
MODULE_DESCRIPTION("USB Wishbone-Serial adapter");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,72 @@
/*
* Xsens MT USB driver
*
* Copyright (C) 2013 Xsens <info@xsens.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
#define XSENS_VID 0x2639
#define MTi_10_IMU_PID 0x0001
#define MTi_20_VRU_PID 0x0002
#define MTi_30_AHRS_PID 0x0003
#define MTi_100_IMU_PID 0x0011
#define MTi_200_VRU_PID 0x0012
#define MTi_300_AHRS_PID 0x0013
#define MTi_G_700_GPS_INS_PID 0x0017
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(XSENS_VID, MTi_10_IMU_PID) },
{ USB_DEVICE(XSENS_VID, MTi_20_VRU_PID) },
{ USB_DEVICE(XSENS_VID, MTi_30_AHRS_PID) },
{ USB_DEVICE(XSENS_VID, MTi_100_IMU_PID) },
{ USB_DEVICE(XSENS_VID, MTi_200_VRU_PID) },
{ USB_DEVICE(XSENS_VID, MTi_300_AHRS_PID) },
{ USB_DEVICE(XSENS_VID, MTi_G_700_GPS_INS_PID) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
static int xsens_mt_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
if (serial->interface->cur_altsetting->desc.bInterfaceNumber == 1)
return 0;
return -ENODEV;
}
static struct usb_serial_driver xsens_mt_device = {
.driver = {
.owner = THIS_MODULE,
.name = "xsens_mt",
},
.id_table = id_table,
.num_ports = 1,
.probe = xsens_mt_probe,
};
static struct usb_serial_driver * const serial_drivers[] = {
&xsens_mt_device, NULL
};
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR("Frans Klaver <frans.klaver@xsens.com>");
MODULE_DESCRIPTION("USB-serial driver for Xsens motion trackers");
MODULE_LICENSE("GPL");