Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
U
USB Relay Box 1
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
image/svg+xml
Discourse
Discourse
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Projects
USB Relay Box 1
Commits
9da7e1fb
Commit
9da7e1fb
authored
Aug 09, 2019
by
Dimitris Lampridis
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[sw] update driver to latest Linux kernel upstream source (b678c56). It now includes GPIO support.
parent
2c2988f8
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
1295 additions
and
423 deletions
+1295
-423
.gitignore
sw/cp210x-driver/.gitignore
+4
-0
cp210x.c
sw/cp210x-driver/cp210x.c
+1291
-423
No files found.
sw/cp210x-driver/.gitignore
0 → 100644
View file @
9da7e1fb
*
!.gitignore
!cp210x.c
!Makefile
sw/cp210x-driver/cp210x.c
View file @
9da7e1fb
// SPDX-License-Identifier: GPL-2.0
/*
/*
* Silicon Laboratories CP210x USB to RS232 serial adaptor driver
* Silicon Laboratories CP210x USB to RS232 serial adaptor driver
*
*
* Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk)
* 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
* Support to set flow control line levels using TIOCMGET and TIOCMSET
* thanks to Karl Hiramoto karl@hiramoto.org. RTSCTS hardware flow
* thanks to Karl Hiramoto karl@hiramoto.org. RTSCTS hardware flow
* control thanks to Munir Nassar nassarmu@real-time.com
* control thanks to Munir Nassar nassarmu@real-time.com
...
@@ -23,11 +20,10 @@
...
@@ -23,11 +20,10 @@
#include <linux/usb.h>
#include <linux/usb.h>
#include <linux/uaccess.h>
#include <linux/uaccess.h>
#include <linux/usb/serial.h>
#include <linux/usb/serial.h>
#include <linux/gpio/driver.h>
#include <linux/bitops.h>
#include <linux/mutex.h>
/*
* Version Information
*/
#define DRIVER_VERSION "v0.09"
#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"
#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"
/*
/*
...
@@ -35,40 +31,43 @@
...
@@ -35,40 +31,43 @@
*/
*/
static
int
cp210x_open
(
struct
tty_struct
*
tty
,
struct
usb_serial_port
*
);
static
int
cp210x_open
(
struct
tty_struct
*
tty
,
struct
usb_serial_port
*
);
static
void
cp210x_close
(
struct
usb_serial_port
*
);
static
void
cp210x_close
(
struct
usb_serial_port
*
);
static
int
cp210x_ioctl
(
struct
tty_struct
*
tty
,
static
void
cp210x_get_termios
(
struct
tty_struct
*
,
struct
usb_serial_port
*
);
unsigned
int
cmd
,
unsigned
long
arg
);
static
void
cp210x_get_termios
(
struct
tty_struct
*
,
struct
usb_serial_port
*
port
);
static
void
cp210x_get_termios_port
(
struct
usb_serial_port
*
port
,
static
void
cp210x_get_termios_port
(
struct
usb_serial_port
*
port
,
unsigned
in
t
*
cflagp
,
unsigned
int
*
baudp
);
tcflag_
t
*
cflagp
,
unsigned
int
*
baudp
);
static
void
cp210x_change_speed
(
struct
tty_struct
*
,
struct
usb_serial_port
*
,
static
void
cp210x_change_speed
(
struct
tty_struct
*
,
struct
usb_serial_port
*
,
struct
ktermios
*
);
struct
ktermios
*
);
static
void
cp210x_set_termios
(
struct
tty_struct
*
,
struct
usb_serial_port
*
,
static
void
cp210x_set_termios
(
struct
tty_struct
*
,
struct
usb_serial_port
*
,
struct
ktermios
*
);
struct
ktermios
*
);
static
bool
cp210x_tx_empty
(
struct
usb_serial_port
*
port
);
static
int
cp210x_tiocmget
(
struct
tty_struct
*
);
static
int
cp210x_tiocmget
(
struct
tty_struct
*
);
static
int
cp210x_tiocmset
(
struct
tty_struct
*
,
unsigned
int
,
unsigned
int
);
static
int
cp210x_tiocmset
(
struct
tty_struct
*
,
unsigned
int
,
unsigned
int
);
static
int
cp210x_tiocmset_port
(
struct
usb_serial_port
*
port
,
static
int
cp210x_tiocmset_port
(
struct
usb_serial_port
*
port
,
unsigned
int
,
unsigned
int
);
unsigned
int
,
unsigned
int
);
static
void
cp210x_break_ctl
(
struct
tty_struct
*
,
int
);
static
void
cp210x_break_ctl
(
struct
tty_struct
*
,
int
);
static
int
cp210x_startup
(
struct
usb_serial
*
);
static
int
cp210x_attach
(
struct
usb_serial
*
);
static
void
cp210x_disconnect
(
struct
usb_serial
*
);
static
void
cp210x_release
(
struct
usb_serial
*
);
static
void
cp210x_release
(
struct
usb_serial
*
);
static
int
cp210x_port_probe
(
struct
usb_serial_port
*
);
static
int
cp210x_port_remove
(
struct
usb_serial_port
*
);
static
void
cp210x_dtr_rts
(
struct
usb_serial_port
*
p
,
int
on
);
static
void
cp210x_dtr_rts
(
struct
usb_serial_port
*
p
,
int
on
);
static
bool
debug
;
static
const
struct
usb_device_id
id_table
[]
=
{
static
const
struct
usb_device_id
id_table
[]
=
{
{
USB_DEVICE
(
0x045B
,
0x0053
)
},
/* Renesas RX610 RX-Stick */
{
USB_DEVICE
(
0x045B
,
0x0053
)
},
/* Renesas RX610 RX-Stick */
{
USB_DEVICE
(
0x0471
,
0x066A
)
},
/* AKTAKOM ACE-1001 cable */
{
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
,
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
(
0x0489
,
0xE003
)
},
/* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
{
USB_DEVICE
(
0x0745
,
0x1000
)
},
/* CipherLab USB CCD Barcode Scanner 1000 */
{
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
(
0x08e6
,
0x5501
)
},
/* Gemalto Prox-PU/CU contactless smartcard reader */
{
USB_DEVICE
(
0x08FD
,
0x000A
)
},
/* Digianswer A/S , ZigBee/802.15.4 MAC Device */
{
USB_DEVICE
(
0x08FD
,
0x000A
)
},
/* Digianswer A/S , ZigBee/802.15.4 MAC Device */
{
USB_DEVICE
(
0x0908
,
0x01FF
)
},
/* Siemens RUGGEDCOM USB Serial Console */
{
USB_DEVICE
(
0x0B00
,
0x3070
)
},
/* Ingenico 3070 */
{
USB_DEVICE
(
0x0BED
,
0x1100
)
},
/* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */
{
USB_DEVICE
(
0x0BED
,
0x1100
)
},
/* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */
{
USB_DEVICE
(
0x0BED
,
0x1101
)
},
/* MEI series 2000 Combo Acceptor */
{
USB_DEVICE
(
0x0BED
,
0x1101
)
},
/* MEI series 2000 Combo Acceptor */
{
USB_DEVICE
(
0x0FCF
,
0x1003
)
},
/* Dynastream ANT development board */
{
USB_DEVICE
(
0x0FCF
,
0x1003
)
},
/* Dynastream ANT development board */
{
USB_DEVICE
(
0x0FCF
,
0x1004
)
},
/* Dynastream ANT2USB */
{
USB_DEVICE
(
0x0FCF
,
0x1004
)
},
/* Dynastream ANT2USB */
{
USB_DEVICE
(
0x0FCF
,
0x1006
)
},
/* Dynastream ANT development board */
{
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
(
0x10A6
,
0xAA26
)
},
/* Knock-off DCU-11 cable */
{
USB_DEVICE
(
0x10AB
,
0x10C5
)
},
/* Siemens MC60 Cable */
{
USB_DEVICE
(
0x10AB
,
0x10C5
)
},
/* Siemens MC60 Cable */
{
USB_DEVICE
(
0x10B5
,
0xAC70
)
},
/* Nokia CA-42 USB */
{
USB_DEVICE
(
0x10B5
,
0xAC70
)
},
/* Nokia CA-42 USB */
...
@@ -81,9 +80,11 @@ static const struct usb_device_id id_table[] = {
...
@@ -81,9 +80,11 @@ static const struct usb_device_id id_table[] = {
{
USB_DEVICE
(
0x10C4
,
0x804E
)
},
/* Software Bisque Paramount ME build-in converter */
{
USB_DEVICE
(
0x10C4
,
0x804E
)
},
/* Software Bisque Paramount ME build-in converter */
{
USB_DEVICE
(
0x10C4
,
0x8053
)
},
/* Enfora EDG1228 */
{
USB_DEVICE
(
0x10C4
,
0x8053
)
},
/* Enfora EDG1228 */
{
USB_DEVICE
(
0x10C4
,
0x8054
)
},
/* Enfora GSM2228 */
{
USB_DEVICE
(
0x10C4
,
0x8054
)
},
/* Enfora GSM2228 */
{
USB_DEVICE
(
0x10C4
,
0x8056
)
},
/* Lorenz Messtechnik devices */
{
USB_DEVICE
(
0x10C4
,
0x8066
)
},
/* Argussoft In-System Programmer */
{
USB_DEVICE
(
0x10C4
,
0x8066
)
},
/* Argussoft In-System Programmer */
{
USB_DEVICE
(
0x10C4
,
0x806F
)
},
/* IMS USB to RS422 Converter Cable */
{
USB_DEVICE
(
0x10C4
,
0x806F
)
},
/* IMS USB to RS422 Converter Cable */
{
USB_DEVICE
(
0x10C4
,
0x807A
)
},
/* Crumb128 board */
{
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
,
0x80CA
)
},
/* Degree Controls Inc */
{
USB_DEVICE
(
0x10C4
,
0x80DD
)
},
/* Tracient RFID */
{
USB_DEVICE
(
0x10C4
,
0x80DD
)
},
/* Tracient RFID */
{
USB_DEVICE
(
0x10C4
,
0x80F6
)
},
/* Suunto sports instrument */
{
USB_DEVICE
(
0x10C4
,
0x80F6
)
},
/* Suunto sports instrument */
...
@@ -92,8 +93,13 @@ static const struct usb_device_id id_table[] = {
...
@@ -92,8 +93,13 @@ static const struct usb_device_id id_table[] = {
{
USB_DEVICE
(
0x10C4
,
0x813F
)
},
/* Tams Master Easy Control */
{
USB_DEVICE
(
0x10C4
,
0x813F
)
},
/* Tams Master Easy Control */
{
USB_DEVICE
(
0x10C4
,
0x814A
)
},
/* West Mountain Radio RIGblaster P&P */
{
USB_DEVICE
(
0x10C4
,
0x814A
)
},
/* West Mountain Radio RIGblaster P&P */
{
USB_DEVICE
(
0x10C4
,
0x814B
)
},
/* West Mountain Radio RIGtalk */
{
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
,
0x8156
)
},
/* B&G H3000 link cable */
{
USB_DEVICE
(
0x10C4
,
0x815E
)
},
/* Helicomm IP-Link 1220-DVM */
{
USB_DEVICE
(
0x10C4
,
0x815E
)
},
/* Helicomm IP-Link 1220-DVM */
{
USB_DEVICE
(
0x10C4
,
0x815F
)
},
/* Timewave HamLinkUSB */
{
USB_DEVICE
(
0x10C4
,
0x817C
)
},
/* CESINEL MEDCAL N Power Quality Monitor */
{
USB_DEVICE
(
0x10C4
,
0x817D
)
},
/* CESINEL MEDCAL NT Power Quality Monitor */
{
USB_DEVICE
(
0x10C4
,
0x817E
)
},
/* CESINEL MEDCAL S Power Quality Monitor */
{
USB_DEVICE
(
0x10C4
,
0x818B
)
},
/* AVIT Research USB to TTL */
{
USB_DEVICE
(
0x10C4
,
0x818B
)
},
/* AVIT Research USB to TTL */
{
USB_DEVICE
(
0x10C4
,
0x819F
)
},
/* MJS USB Toslink Switcher */
{
USB_DEVICE
(
0x10C4
,
0x819F
)
},
/* MJS USB Toslink Switcher */
{
USB_DEVICE
(
0x10C4
,
0x81A6
)
},
/* ThinkOptics WavIt */
{
USB_DEVICE
(
0x10C4
,
0x81A6
)
},
/* ThinkOptics WavIt */
...
@@ -101,6 +107,7 @@ static const struct usb_device_id id_table[] = {
...
@@ -101,6 +107,7 @@ static const struct usb_device_id id_table[] = {
{
USB_DEVICE
(
0x10C4
,
0x81AC
)
},
/* MSD Dash Hawk */
{
USB_DEVICE
(
0x10C4
,
0x81AC
)
},
/* MSD Dash Hawk */
{
USB_DEVICE
(
0x10C4
,
0x81AD
)
},
/* INSYS USB Modem */
{
USB_DEVICE
(
0x10C4
,
0x81AD
)
},
/* INSYS USB Modem */
{
USB_DEVICE
(
0x10C4
,
0x81C8
)
},
/* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */
{
USB_DEVICE
(
0x10C4
,
0x81C8
)
},
/* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */
{
USB_DEVICE
(
0x10C4
,
0x81D7
)
},
/* IAI Corp. RCB-CV-USB USB to RS485 Adaptor */
{
USB_DEVICE
(
0x10C4
,
0x81E2
)
},
/* Lipowsky Industrie Elektronik GmbH, Baby-LIN */
{
USB_DEVICE
(
0x10C4
,
0x81E2
)
},
/* Lipowsky Industrie Elektronik GmbH, Baby-LIN */
{
USB_DEVICE
(
0x10C4
,
0x81E7
)
},
/* Aerocomm Radio */
{
USB_DEVICE
(
0x10C4
,
0x81E7
)
},
/* Aerocomm Radio */
{
USB_DEVICE
(
0x10C4
,
0x81E8
)
},
/* Zephyr Bioharness */
{
USB_DEVICE
(
0x10C4
,
0x81E8
)
},
/* Zephyr Bioharness */
...
@@ -108,7 +115,12 @@ static const struct usb_device_id id_table[] = {
...
@@ -108,7 +115,12 @@ static const struct usb_device_id id_table[] = {
{
USB_DEVICE
(
0x10C4
,
0x8218
)
},
/* Lipowsky Industrie Elektronik GmbH, HARP-1 */
{
USB_DEVICE
(
0x10C4
,
0x8218
)
},
/* Lipowsky Industrie Elektronik GmbH, HARP-1 */
{
USB_DEVICE
(
0x10C4
,
0x822B
)
},
/* Modem EDGE(GSM) Comander 2 */
{
USB_DEVICE
(
0x10C4
,
0x822B
)
},
/* Modem EDGE(GSM) Comander 2 */
{
USB_DEVICE
(
0x10C4
,
0x826B
)
},
/* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */
{
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
,
0x8293
)
},
/* Telegesis ETRX2USB */
{
USB_DEVICE
(
0x10C4
,
0x82EF
)
},
/* CESINEL FALCO 6105 AC Power Supply */
{
USB_DEVICE
(
0x10C4
,
0x82F1
)
},
/* CESINEL MEDCAL EFD Earth Fault Detector */
{
USB_DEVICE
(
0x10C4
,
0x82F2
)
},
/* CESINEL MEDCAL ST Network Analyzer */
{
USB_DEVICE
(
0x10C4
,
0x82F4
)
},
/* Starizona MicroTouch */
{
USB_DEVICE
(
0x10C4
,
0x82F9
)
},
/* Procyon AVS */
{
USB_DEVICE
(
0x10C4
,
0x82F9
)
},
/* Procyon AVS */
{
USB_DEVICE
(
0x10C4
,
0x8341
)
},
/* Siemens MC35PU GPRS Modem */
{
USB_DEVICE
(
0x10C4
,
0x8341
)
},
/* Siemens MC35PU GPRS Modem */
{
USB_DEVICE
(
0x10C4
,
0x8382
)
},
/* Cygnal Integrated Products, Inc. */
{
USB_DEVICE
(
0x10C4
,
0x8382
)
},
/* Cygnal Integrated Products, Inc. */
...
@@ -117,25 +129,59 @@ static const struct usb_device_id id_table[] = {
...
@@ -117,25 +129,59 @@ static const struct usb_device_id id_table[] = {
{
USB_DEVICE
(
0x10C4
,
0x8411
)
},
/* Kyocera GPS Module */
{
USB_DEVICE
(
0x10C4
,
0x8411
)
},
/* Kyocera GPS Module */
{
USB_DEVICE
(
0x10C4
,
0x8418
)
},
/* IRZ Automation Teleport SG-10 GSM/GPRS Modem */
{
USB_DEVICE
(
0x10C4
,
0x8418
)
},
/* IRZ Automation Teleport SG-10 GSM/GPRS Modem */
{
USB_DEVICE
(
0x10C4
,
0x846E
)
},
/* BEI USB Sensor Interface (VCP) */
{
USB_DEVICE
(
0x10C4
,
0x846E
)
},
/* BEI USB Sensor Interface (VCP) */
{
USB_DEVICE
(
0x10C4
,
0x8470
)
},
/* Juniper Networks BX Series System Console */
{
USB_DEVICE
(
0x10C4
,
0x8477
)
},
/* Balluff RFID */
{
USB_DEVICE
(
0x10C4
,
0x8477
)
},
/* Balluff RFID */
{
USB_DEVICE
(
0x10C4
,
0x84B6
)
},
/* Starizona Hyperion */
{
USB_DEVICE
(
0x10C4
,
0x851E
)
},
/* CESINEL MEDCAL PT Network Analyzer */
{
USB_DEVICE
(
0x10C4
,
0x85A7
)
},
/* LifeScan OneTouch Verio IQ */
{
USB_DEVICE
(
0x10C4
,
0x85B8
)
},
/* CESINEL ReCon T Energy Logger */
{
USB_DEVICE
(
0x10C4
,
0x85EA
)
},
/* AC-Services IBUS-IF */
{
USB_DEVICE
(
0x10C4
,
0x85EA
)
},
/* AC-Services IBUS-IF */
{
USB_DEVICE
(
0x10C4
,
0x85EB
)
},
/* AC-Services CIS-IBUS */
{
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
,
0x8664
)
},
/* AC-Services CAN-IF */
{
USB_DEVICE
(
0x10C4
,
0x8665
)
},
/* AC-Services OBD-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
,
0x88FB
)
},
/* CESINEL MEDCAL STII Network Analyzer */
{
USB_DEVICE
(
0x10C4
,
0x8938
)
},
/* CESINEL MEDCAL S II Network Analyzer */
{
USB_DEVICE
(
0x10C4
,
0x8946
)
},
/* Ketra N1 Wireless Interface */
{
USB_DEVICE
(
0x10C4
,
0x8962
)
},
/* Brim Brothers charging dock */
{
USB_DEVICE
(
0x10C4
,
0x8977
)
},
/* CEL MeshWorks DevKit Device */
{
USB_DEVICE
(
0x10C4
,
0x8998
)
},
/* KCF Technologies PRN */
{
USB_DEVICE
(
0x10C4
,
0x89A4
)
},
/* CESINEL FTBC Flexible Thyristor Bridge Controller */
{
USB_DEVICE
(
0x10C4
,
0x89FB
)
},
/* Qivicon ZigBee USB Radio Stick */
{
USB_DEVICE
(
0x10C4
,
0x8A2A
)
},
/* HubZ dual ZigBee and Z-Wave dongle */
{
USB_DEVICE
(
0x10C4
,
0x8A5E
)
},
/* CEL EM3588 ZigBee USB Stick Long Range */
{
USB_DEVICE
(
0x10C4
,
0x8B34
)
},
/* Qivicon ZigBee USB Radio Stick */
{
USB_DEVICE
(
0x10C4
,
0xEA60
)
},
/* Silicon Labs factory default */
{
USB_DEVICE
(
0x10C4
,
0xEA60
)
},
/* Silicon Labs factory default */
{
USB_DEVICE
(
0x10C4
,
0xEA61
)
},
/* Silicon Labs factory default */
{
USB_DEVICE
(
0x10C4
,
0xEA61
)
},
/* Silicon Labs factory default */
{
USB_DEVICE
(
0x10C4
,
0xEA63
)
},
/* Silicon Labs Windows Update (CP2101-4/CP2102N) */
{
USB_DEVICE
(
0x10C4
,
0xEA70
)
},
/* 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
,
0xEA71
)
},
/* Infinity GPS-MIC-1 Radio Monophone */
{
USB_DEVICE
(
0x10C4
,
0xEA7A
)
},
/* Silicon Labs Windows Update (CP2105) */
{
USB_DEVICE
(
0x10C4
,
0xEA7B
)
},
/* Silicon Labs Windows Update (CP2108) */
{
USB_DEVICE
(
0x10C4
,
0xF001
)
},
/* Elan Digital Systems USBscope50 */
{
USB_DEVICE
(
0x10C4
,
0xF001
)
},
/* Elan Digital Systems USBscope50 */
{
USB_DEVICE
(
0x10C4
,
0xF002
)
},
/* Elan Digital Systems USBwave12 */
{
USB_DEVICE
(
0x10C4
,
0xF002
)
},
/* Elan Digital Systems USBwave12 */
{
USB_DEVICE
(
0x10C4
,
0xF003
)
},
/* Elan Digital Systems USBpulse100 */
{
USB_DEVICE
(
0x10C4
,
0xF003
)
},
/* Elan Digital Systems USBpulse100 */
{
USB_DEVICE
(
0x10C4
,
0xF004
)
},
/* Elan Digital Systems USBcount50 */
{
USB_DEVICE
(
0x10C4
,
0xF004
)
},
/* Elan Digital Systems USBcount50 */
{
USB_DEVICE
(
0x10C5
,
0xEA61
)
},
/* Silicon Labs MobiData GPRS USB Modem */
{
USB_DEVICE
(
0x10C5
,
0xEA61
)
},
/* Silicon Labs MobiData GPRS USB Modem */
{
USB_DEVICE
(
0x10CE
,
0xEA6A
)
},
/* Silicon Labs MobiData GPRS USB Modem 100EU */
{
USB_DEVICE
(
0x10CE
,
0xEA6A
)
},
/* Silicon Labs MobiData GPRS USB Modem 100EU */
{
USB_DEVICE
(
0x12B8
,
0xEC60
)
},
/* Link G4 ECU */
{
USB_DEVICE
(
0x12B8
,
0xEC62
)
},
/* Link G4+ ECU */
{
USB_DEVICE
(
0x13AD
,
0x9999
)
},
/* Baltech card reader */
{
USB_DEVICE
(
0x13AD
,
0x9999
)
},
/* Baltech card reader */
{
USB_DEVICE
(
0x1555
,
0x0004
)
},
/* Owen AC4 USB-RS485 Converter */
{
USB_DEVICE
(
0x1555
,
0x0004
)
},
/* Owen AC4 USB-RS485 Converter */
{
USB_DEVICE
(
0x155A
,
0x1006
)
},
/* ELDAT Easywave RX09 */
{
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
,
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
(
0x16D6
,
0x0001
)
},
/* Jablotron serial interface */
{
USB_DEVICE
(
0x16DC
,
0x0010
)
},
/* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */
{
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
,
0x0011
)
},
/* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */
...
@@ -146,46 +192,92 @@ static const struct usb_device_id id_table[] = {
...
@@ -146,46 +192,92 @@ static const struct usb_device_id id_table[] = {
{
USB_DEVICE
(
0x17F4
,
0xAAAA
)
},
/* Wavesense Jazz blood glucose meter */
{
USB_DEVICE
(
0x17F4
,
0xAAAA
)
},
/* Wavesense Jazz blood glucose meter */
{
USB_DEVICE
(
0x1843
,
0x0200
)
},
/* Vaisala USB Instrument Cable */
{
USB_DEVICE
(
0x1843
,
0x0200
)
},
/* Vaisala USB Instrument Cable */
{
USB_DEVICE
(
0x18EF
,
0xE00F
)
},
/* ELV USB-I2C-Interface */
{
USB_DEVICE
(
0x18EF
,
0xE00F
)
},
/* ELV USB-I2C-Interface */
{
USB_DEVICE
(
0x18EF
,
0xE025
)
},
/* ELV Marble Sound Board 1 */
{
USB_DEVICE
(
0x18EF
,
0xE030
)
},
/* ELV ALC 8xxx Battery Charger */
{
USB_DEVICE
(
0x18EF
,
0xE032
)
},
/* ELV TFD500 Data Logger */
{
USB_DEVICE
(
0x1901
,
0x0190
)
},
/* GE B850 CP2105 Recorder interface */
{
USB_DEVICE
(
0x1901
,
0x0193
)
},
/* GE B650 CP2104 PMC interface */
{
USB_DEVICE
(
0x1901
,
0x0194
)
},
/* GE Healthcare Remote Alarm Box */
{
USB_DEVICE
(
0x1901
,
0x0195
)
},
/* GE B850/B650/B450 CP2104 DP UART interface */
{
USB_DEVICE
(
0x1901
,
0x0196
)
},
/* GE B850 CP2105 DP UART interface */
{
USB_DEVICE
(
0x19CF
,
0x3000
)
},
/* Parrot NMEA GPS Flight Recorder */
{
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
(
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
(
0x2626
,
0xEA60
)
},
/* Aruba Networks 7xxx USB Serial Console */
{
USB_DEVICE
(
0x3195
,
0xF190
)
},
/* Link Instruments MSO-19 */
{
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
(
0x3923
,
0x7A0B
)
},
/* National Instruments USB Serial Console */
{
USB_DEVICE
(
0x413C
,
0x9500
)
},
/* DW700 GPS USB interface */
{
USB_DEVICE
(
0x413C
,
0x9500
)
},
/* DW700 GPS USB interface */
{
}
/* Terminating Entry */
{
}
/* Terminating Entry */
};
};
MODULE_DEVICE_TABLE
(
usb
,
id_table
);
MODULE_DEVICE_TABLE
(
usb
,
id_table
);
struct
cp210x_port_private
{
struct
cp210x_serial_private
{
__u8
bInterfaceNumber
;
#ifdef CONFIG_GPIOLIB
__u8
bPartNumber
;
struct
gpio_chip
gc
;
bool
gpio_registered
;
u8
gpio_pushpull
;
u8
gpio_altfunc
;
u8
gpio_input
;
#endif
u8
partnum
;
speed_t
min_speed
;
speed_t
max_speed
;
bool
use_actual_rate
;
};
};
/*static struct usb_driver cp210x_driver = {
struct
cp210x_port_private
{
.name = "cp210x",
__u8
bInterfaceNumber
;
.probe = usb_serial_probe,
bool
has_swapped_line_ctl
;
.disconnect = usb_serial_disconnect,
};
.id_table = id_table,
.no_dynamic_id = 1,
};*/
static
struct
usb_serial_driver
cp210x_device
=
{
static
struct
usb_serial_driver
cp210x_device
=
{
.
driver
=
{
.
driver
=
{
.
owner
=
THIS_MODULE
,
.
owner
=
THIS_MODULE
,
.
name
=
"cp210x"
,
.
name
=
"cp210x"
,
},
},
// .usb_driver = &cp210x_driver,
.
id_table
=
id_table
,
.
id_table
=
id_table
,
.
num_ports
=
1
,
.
num_ports
=
1
,
.
bulk_in_size
=
256
,
.
bulk_in_size
=
256
,
.
bulk_out_size
=
256
,
.
bulk_out_size
=
256
,
.
open
=
cp210x_open
,
.
open
=
cp210x_open
,
.
close
=
cp210x_close
,
.
close
=
cp210x_close
,
.
ioctl
=
cp210x_ioctl
,
.
break_ctl
=
cp210x_break_ctl
,
.
break_ctl
=
cp210x_break_ctl
,
.
set_termios
=
cp210x_set_termios
,
.
set_termios
=
cp210x_set_termios
,
.
tiocmget
=
cp210x_tiocmget
,
.
tx_empty
=
cp210x_tx_empty
,
.
tiocmget
=
cp210x_tiocmget
,
.
tiocmset
=
cp210x_tiocmset
,
.
tiocmset
=
cp210x_tiocmset
,
.
attach
=
cp210x_startup
,
.
attach
=
cp210x_attach
,
.
release
=
cp210x_release
,
.
disconnect
=
cp210x_disconnect
,
.
release
=
cp210x_release
,
.
port_probe
=
cp210x_port_probe
,
.
port_remove
=
cp210x_port_remove
,
.
dtr_rts
=
cp210x_dtr_rts
.
dtr_rts
=
cp210x_dtr_rts
};
};
...
@@ -193,19 +285,6 @@ static struct usb_serial_driver * const serial_drivers[] = {
...
@@ -193,19 +285,6 @@ static struct usb_serial_driver * const serial_drivers[] = {
&
cp210x_device
,
NULL
&
cp210x_device
,
NULL
};
};
/* Part number definitions */
#define CP2101_PARTNUM 0x01
#define CP2102_PARTNUM 0x02
#define CP2103_PARTNUM 0x03
#define CP2104_PARTNUM 0x04
#define CP2105_PARTNUM 0x05
#define CP2108_PARTNUM 0x08
/* IOCTLs */
#define IOCTL_GPIOGET 0x8000
#define IOCTL_GPIOSET 0x8001
/* Config request types */
/* Config request types */
#define REQTYPE_HOST_TO_INTERFACE 0x41
#define REQTYPE_HOST_TO_INTERFACE 0x41
#define REQTYPE_INTERFACE_TO_HOST 0xc1
#define REQTYPE_INTERFACE_TO_HOST 0xc1
...
@@ -245,11 +324,6 @@ static struct usb_serial_driver * const serial_drivers[] = {
...
@@ -245,11 +324,6 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define UART_ENABLE 0x0001
#define UART_ENABLE 0x0001
#define UART_DISABLE 0x0000
#define UART_DISABLE 0x0000
/* CP210X_VENDOR_SPECIFIC */
#define CP210X_WRITE_LATCH 0x37E1
#define CP210X_READ_LATCH 0x00C2
#define CP210X_GET_PARTNUM 0x370B
/* CP210X_(SET|GET)_BAUDDIV */
/* CP210X_(SET|GET)_BAUDDIV */
#define BAUD_RATE_GEN_FREQ 0x384000
#define BAUD_RATE_GEN_FREQ 0x384000
...
@@ -287,155 +361,454 @@ static struct usb_serial_driver * const serial_drivers[] = {
...
@@ -287,155 +361,454 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define CONTROL_WRITE_DTR 0x0100
#define CONTROL_WRITE_DTR 0x0100
#define CONTROL_WRITE_RTS 0x0200
#define CONTROL_WRITE_RTS 0x0200
/* CP210X_VENDOR_SPECIFIC values */
#define CP210X_READ_2NCONFIG 0x000E
#define CP210X_READ_LATCH 0x00C2
#define CP210X_GET_PARTNUM 0x370B
#define CP210X_GET_PORTCONFIG 0x370C
#define CP210X_GET_DEVICEMODE 0x3711
#define CP210X_WRITE_LATCH 0x37E1
/* Part number definitions */
#define CP210X_PARTNUM_CP2101 0x01
#define CP210X_PARTNUM_CP2102 0x02
#define CP210X_PARTNUM_CP2103 0x03
#define CP210X_PARTNUM_CP2104 0x04
#define CP210X_PARTNUM_CP2105 0x05
#define CP210X_PARTNUM_CP2108 0x08
#define CP210X_PARTNUM_CP2102N_QFN28 0x20
#define CP210X_PARTNUM_CP2102N_QFN24 0x21
#define CP210X_PARTNUM_CP2102N_QFN20 0x22
#define CP210X_PARTNUM_UNKNOWN 0xFF
/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
struct
cp210x_comm_status
{
__le32
ulErrors
;
__le32
ulHoldReasons
;
__le32
ulAmountInInQueue
;
__le32
ulAmountInOutQueue
;
u8
bEofReceived
;
u8
bWaitForImmediate
;
u8
bReserved
;
}
__packed
;
/*
* CP210X_PURGE - 16 bits passed in wValue of USB request.
* SiLabs app note AN571 gives a strange description of the 4 bits:
* bit 0 or bit 2 clears the transmit queue and 1 or 3 receive.
* writing 1 to all, however, purges cp2108 well enough to avoid the hang.
*/
#define PURGE_ALL 0x000f
/* CP210X_GET_FLOW/CP210X_SET_FLOW read/write these 0x10 bytes */
struct
cp210x_flow_ctl
{
__le32
ulControlHandshake
;
__le32
ulFlowReplace
;
__le32
ulXonLimit
;
__le32
ulXoffLimit
;
}
__packed
;
/* cp210x_flow_ctl::ulControlHandshake */
#define CP210X_SERIAL_DTR_MASK GENMASK(1, 0)
#define CP210X_SERIAL_DTR_SHIFT(_mode) (_mode)
#define CP210X_SERIAL_CTS_HANDSHAKE BIT(3)
#define CP210X_SERIAL_DSR_HANDSHAKE BIT(4)
#define CP210X_SERIAL_DCD_HANDSHAKE BIT(5)
#define CP210X_SERIAL_DSR_SENSITIVITY BIT(6)
/* values for cp210x_flow_ctl::ulControlHandshake::CP210X_SERIAL_DTR_MASK */
#define CP210X_SERIAL_DTR_INACTIVE 0
#define CP210X_SERIAL_DTR_ACTIVE 1
#define CP210X_SERIAL_DTR_FLOW_CTL 2
/* cp210x_flow_ctl::ulFlowReplace */
#define CP210X_SERIAL_AUTO_TRANSMIT BIT(0)
#define CP210X_SERIAL_AUTO_RECEIVE BIT(1)
#define CP210X_SERIAL_ERROR_CHAR BIT(2)
#define CP210X_SERIAL_NULL_STRIPPING BIT(3)
#define CP210X_SERIAL_BREAK_CHAR BIT(4)
#define CP210X_SERIAL_RTS_MASK GENMASK(7, 6)
#define CP210X_SERIAL_RTS_SHIFT(_mode) (_mode << 6)
#define CP210X_SERIAL_XOFF_CONTINUE BIT(31)
/* values for cp210x_flow_ctl::ulFlowReplace::CP210X_SERIAL_RTS_MASK */
#define CP210X_SERIAL_RTS_INACTIVE 0
#define CP210X_SERIAL_RTS_ACTIVE 1
#define CP210X_SERIAL_RTS_FLOW_CTL 2
/* CP210X_VENDOR_SPECIFIC, CP210X_GET_DEVICEMODE call reads these 0x2 bytes. */
struct
cp210x_pin_mode
{
u8
eci
;
u8
sci
;
}
__packed
;
#define CP210X_PIN_MODE_MODEM 0
#define CP210X_PIN_MODE_GPIO BIT(0)
/*
/*
* cp210x_get_config
* CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0xf bytes
* Reads from the CP210x configuration registers
* on a CP2105 chip. Structure needs padding due to unused/unspecified bytes.
* '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
requestType
,
struct
cp210x_dual_port_config
{
u8
request
,
int
value
,
unsigned
int
*
data
,
int
size
)
__le16
gpio_mode
;
u8
__pad0
[
2
];
__le16
reset_state
;
u8
__pad1
[
4
];
__le16
suspend_state
;
u8
sci_cfg
;
u8
eci_cfg
;
u8
device_cfg
;
}
__packed
;
/*
* CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0xd bytes
* on a CP2104 chip. Structure needs padding due to unused/unspecified bytes.
*/
struct
cp210x_single_port_config
{
__le16
gpio_mode
;
u8
__pad0
[
2
];
__le16
reset_state
;
u8
__pad1
[
4
];
__le16
suspend_state
;
u8
device_cfg
;
}
__packed
;
/* GPIO modes */
#define CP210X_SCI_GPIO_MODE_OFFSET 9
#define CP210X_SCI_GPIO_MODE_MASK GENMASK(11, 9)
#define CP210X_ECI_GPIO_MODE_OFFSET 2
#define CP210X_ECI_GPIO_MODE_MASK GENMASK(3, 2)
#define CP210X_GPIO_MODE_OFFSET 8
#define CP210X_GPIO_MODE_MASK GENMASK(11, 8)
/* CP2105 port configuration values */
#define CP2105_GPIO0_TXLED_MODE BIT(0)
#define CP2105_GPIO1_RXLED_MODE BIT(1)
#define CP2105_GPIO1_RS485_MODE BIT(2)
/* CP2104 port configuration values */
#define CP2104_GPIO0_TXLED_MODE BIT(0)
#define CP2104_GPIO1_RXLED_MODE BIT(1)
#define CP2104_GPIO2_RS485_MODE BIT(2)
/* CP2102N configuration array indices */
#define CP210X_2NCONFIG_CONFIG_VERSION_IDX 2
#define CP210X_2NCONFIG_GPIO_MODE_IDX 581
#define CP210X_2NCONFIG_GPIO_RSTLATCH_IDX 587
#define CP210X_2NCONFIG_GPIO_CONTROL_IDX 600
/* CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x2 bytes. */
struct
cp210x_gpio_write
{
u8
mask
;
u8
state
;
}
__packed
;
/*
* Helper to get interface number when we only have struct usb_serial.
*/
static
u8
cp210x_interface_num
(
struct
usb_serial
*
serial
)
{
struct
usb_host_interface
*
cur_altsetting
;
cur_altsetting
=
serial
->
interface
->
cur_altsetting
;
return
cur_altsetting
->
desc
.
bInterfaceNumber
;
}
/*
* Reads a variable-sized block of CP210X_ registers, identified by req.
* Returns data into buf in native USB byte order.
*/
static
int
cp210x_read_reg_block
(
struct
usb_serial_port
*
port
,
u8
req
,
void
*
buf
,
int
bufsize
)
{
{
struct
usb_serial
*
serial
=
port
->
serial
;
struct
usb_serial
*
serial
=
port
->
serial
;
struct
cp210x_port_private
*
port_priv
=
usb_get_serial_port_data
(
port
);
struct
cp210x_port_private
*
port_priv
=
usb_get_serial_port_data
(
port
);
__le32
*
buf
;
void
*
dmabuf
;
int
result
,
i
,
length
;
int
result
;
/* Number of integers required to contain the array */
length
=
(((
size
-
1
)
|
3
)
+
1
)
/
4
;
buf
=
kcalloc
(
length
,
sizeof
(
__le32
),
GFP_KERNEL
);
dmabuf
=
kmalloc
(
bufsize
,
GFP_KERNEL
);
if
(
!
buf
)
{
if
(
!
dmabuf
)
{
dev_err
(
&
port
->
dev
,
"%s - out of memory.
\n
"
,
__func__
);
/*
* FIXME Some callers don't bother to check for error,
* at least give them consistent junk until they are fixed
*/
memset
(
buf
,
0
,
bufsize
);
return
-
ENOMEM
;
return
-
ENOMEM
;
}
}
/* Issue the request, attempting to read 'size' bytes */
result
=
usb_control_msg
(
serial
->
dev
,
usb_rcvctrlpipe
(
serial
->
dev
,
0
),
result
=
usb_control_msg
(
serial
->
dev
,
usb_rcvctrlpipe
(
serial
->
dev
,
0
),
request
,
requestType
,
value
,
req
,
REQTYPE_INTERFACE_TO_HOST
,
0
,
port_priv
->
bInterfaceNumber
,
buf
,
size
,
300
);
port_priv
->
bInterfaceNumber
,
dmabuf
,
bufsize
,
USB_CTRL_SET_TIMEOUT
);
/* Convert data into an array of integers */
if
(
result
==
bufsize
)
{
for
(
i
=
0
;
i
<
length
;
i
++
)
memcpy
(
buf
,
dmabuf
,
bufsize
);
data
[
i
]
=
le32_to_cpu
(
buf
[
i
]);
result
=
0
;
}
else
{
dev_err
(
&
port
->
dev
,
"failed get req 0x%x size %d status: %d
\n
"
,
req
,
bufsize
,
result
);
if
(
result
>=
0
)
result
=
-
EIO
;
/*
* FIXME Some callers don't bother to check for error,
* at least give them consistent junk until they are fixed
*/
memset
(
buf
,
0
,
bufsize
);
}
kfree
(
buf
);
kfree
(
dma
buf
);
if
(
result
!=
size
)
{
return
result
;
dbg
(
"%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
;
/*
* Reads any 32-bit CP210X_ register identified by req.
*/
static
int
cp210x_read_u32_reg
(
struct
usb_serial_port
*
port
,
u8
req
,
u32
*
val
)
{
__le32
le32_val
;
int
err
;
err
=
cp210x_read_reg_block
(
port
,
req
,
&
le32_val
,
sizeof
(
le32_val
));
if
(
err
)
{
/*
* FIXME Some callers don't bother to check for error,
* at least give them consistent junk until they are fixed
*/
*
val
=
0
;
return
err
;
}
}
*
val
=
le32_to_cpu
(
le32_val
);
return
0
;
}
/*
* Reads any 16-bit CP210X_ register identified by req.
*/
static
int
cp210x_read_u16_reg
(
struct
usb_serial_port
*
port
,
u8
req
,
u16
*
val
)
{
__le16
le16_val
;
int
err
;
err
=
cp210x_read_reg_block
(
port
,
req
,
&
le16_val
,
sizeof
(
le16_val
));
if
(
err
)
return
err
;
*
val
=
le16_to_cpu
(
le16_val
);
return
0
;
return
0
;
}
}
/*
/*
* cp210x_set_config
* Reads any 8-bit CP210X_ register identified by req.
* Writes to the CP210x configuration registers
*/
* Values less than 16 bits wide are sent directly
static
int
cp210x_read_u8_reg
(
struct
usb_serial_port
*
port
,
u8
req
,
u8
*
val
)
* 'size' is specified in bytes.
{
return
cp210x_read_reg_block
(
port
,
req
,
val
,
sizeof
(
*
val
));
}
/*
* Reads a variable-sized vendor block of CP210X_ registers, identified by val.
* Returns data into buf in native USB byte order.
*/
static
int
cp210x_read_vendor_block
(
struct
usb_serial
*
serial
,
u8
type
,
u16
val
,
void
*
buf
,
int
bufsize
)
{
void
*
dmabuf
;
int
result
;
dmabuf
=
kmalloc
(
bufsize
,
GFP_KERNEL
);
if
(
!
dmabuf
)
return
-
ENOMEM
;
result
=
usb_control_msg
(
serial
->
dev
,
usb_rcvctrlpipe
(
serial
->
dev
,
0
),
CP210X_VENDOR_SPECIFIC
,
type
,
val
,
cp210x_interface_num
(
serial
),
dmabuf
,
bufsize
,
USB_CTRL_GET_TIMEOUT
);
if
(
result
==
bufsize
)
{
memcpy
(
buf
,
dmabuf
,
bufsize
);
result
=
0
;
}
else
{
dev_err
(
&
serial
->
interface
->
dev
,
"failed to get vendor val 0x%04x size %d: %d
\n
"
,
val
,
bufsize
,
result
);
if
(
result
>=
0
)
result
=
-
EIO
;
}
kfree
(
dmabuf
);
return
result
;
}
/*
* Writes any 16-bit CP210X_ register (req) whose value is passed
* entirely in the wValue field of the USB request.
*/
*/
static
int
cp210x_set_config
(
struct
usb_serial_port
*
port
,
u8
requestType
,
static
int
cp210x_write_u16_reg
(
struct
usb_serial_port
*
port
,
u8
req
,
u16
val
)
u8
request
,
int
value
,
unsigned
int
*
data
,
int
size
)
{
{
struct
usb_serial
*
serial
=
port
->
serial
;
struct
usb_serial
*
serial
=
port
->
serial
;
struct
cp210x_port_private
*
port_priv
=
usb_get_serial_port_data
(
port
);
struct
cp210x_port_private
*
port_priv
=
usb_get_serial_port_data
(
port
);
__le32
*
buf
=
NULL
;
int
result
;
int
result
,
i
,
length
=
0
;
if
(
size
)
{
/* Number of integers required to contain the array */
length
=
(((
size
-
1
)
|
3
)
+
1
)
/
4
;
buf
=
kmalloc
(
length
*
sizeof
(
__le32
),
GFP_KERNEL
);
if
(
!
buf
)
{
dev_err
(
&
port
->
dev
,
"%s - out of memory.
\n
"
,
__func__
);
return
-
ENOMEM
;
}
/* Array of integers into bytes */
result
=
usb_control_msg
(
serial
->
dev
,
usb_sndctrlpipe
(
serial
->
dev
,
0
),
for
(
i
=
0
;
i
<
length
;
i
++
)
req
,
REQTYPE_HOST_TO_INTERFACE
,
val
,
buf
[
i
]
=
cpu_to_le32
(
data
[
i
]);
port_priv
->
bInterfaceNumber
,
NULL
,
0
,
USB_CTRL_SET_TIMEOUT
);
if
(
result
<
0
)
{
dev_err
(
&
port
->
dev
,
"failed set request 0x%x status: %d
\n
"
,
req
,
result
);
}
}
result
=
usb_control_msg
(
serial
->
dev
,
return
result
;
usb_sndctrlpipe
(
serial
->
dev
,
0
),
}
request
,
requestType
,
value
,
port_priv
->
bInterfaceNumber
,
buf
,
size
,
300
);
/*
* Writes a variable-sized block of CP210X_ registers, identified by req.
* Data in buf must be in native USB byte order.
*/
static
int
cp210x_write_reg_block
(
struct
usb_serial_port
*
port
,
u8
req
,
void
*
buf
,
int
bufsize
)
{
struct
usb_serial
*
serial
=
port
->
serial
;
struct
cp210x_port_private
*
port_priv
=
usb_get_serial_port_data
(
port
);
void
*
dmabuf
;
int
result
;
dmabuf
=
kmemdup
(
buf
,
bufsize
,
GFP_KERNEL
);
if
(
!
dmabuf
)
return
-
ENOMEM
;
if
(
buf
)
result
=
usb_control_msg
(
serial
->
dev
,
usb_sndctrlpipe
(
serial
->
dev
,
0
),
kfree
(
buf
);
req
,
REQTYPE_HOST_TO_INTERFACE
,
0
,
port_priv
->
bInterfaceNumber
,
dmabuf
,
bufsize
,
USB_CTRL_SET_TIMEOUT
);
if
(
result
!=
size
)
{
kfree
(
dmabuf
);
dbg
(
"%s - Unable to send request, "
"request=0x%x size=%d result=%d
\n
"
,
__func__
,
request
,
size
,
result
);
if
(
result
>
0
)
result
=
-
EPROTO
;
return
result
;
if
(
result
==
bufsize
)
{
result
=
0
;
}
else
{
dev_err
(
&
port
->
dev
,
"failed set req 0x%x size %d status: %d
\n
"
,
req
,
bufsize
,
result
);
if
(
result
>=
0
)
result
=
-
EIO
;
}
}
return
0
;
return
result
;
}
}
/*
/*
* cp210x_quantise_baudrate
* Writes any 32-bit CP210X_ register identified by req.
* Quantises the baud rate as per AN205 Table 1
*/
*/
static
unsigned
int
cp210x_quantise_baudrate
(
unsigned
int
baud
)
{
static
int
cp210x_write_u32_reg
(
struct
usb_serial_port
*
port
,
u8
req
,
u32
val
)
if
(
baud
<=
300
)
{
baud
=
300
;
__le32
le32_val
;
else
if
(
baud
<=
600
)
baud
=
600
;
else
if
(
baud
<=
1200
)
baud
=
1200
;
le32_val
=
cpu_to_le32
(
val
);
else
if
(
baud
<=
1800
)
baud
=
1800
;
else
if
(
baud
<=
2400
)
baud
=
2400
;
return
cp210x_write_reg_block
(
port
,
req
,
&
le32_val
,
sizeof
(
le32_val
));
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
)
#ifdef CONFIG_GPIOLIB
/*
* Writes a variable-sized vendor block of CP210X_ registers, identified by val.
* Data in buf must be in native USB byte order.
*/
static
int
cp210x_write_vendor_block
(
struct
usb_serial
*
serial
,
u8
type
,
u16
val
,
void
*
buf
,
int
bufsize
)
{
{
void
*
dmabuf
;
int
result
;
int
result
;
dbg
(
"%s - port %d"
,
__func__
,
port
->
number
);
dmabuf
=
kmemdup
(
buf
,
bufsize
,
GFP_KERNEL
);
if
(
!
dmabuf
)
return
-
ENOMEM
;
result
=
usb_control_msg
(
serial
->
dev
,
usb_sndctrlpipe
(
serial
->
dev
,
0
),
CP210X_VENDOR_SPECIFIC
,
type
,
val
,
cp210x_interface_num
(
serial
),
dmabuf
,
bufsize
,
USB_CTRL_SET_TIMEOUT
);
kfree
(
dmabuf
);
if
(
result
==
bufsize
)
{
result
=
0
;
}
else
{
dev_err
(
&
serial
->
interface
->
dev
,
"failed to set vendor val 0x%04x size %d: %d
\n
"
,
val
,
bufsize
,
result
);
if
(
result
>=
0
)
result
=
-
EIO
;
}
return
result
;
}
#endif
/*
* Detect CP2108 GET_LINE_CTL bug and activate workaround.
* Write a known good value 0x800, read it back.
* If it comes back swapped the bug is detected.
* Preserve the original register value.
*/
static
int
cp210x_detect_swapped_line_ctl
(
struct
usb_serial_port
*
port
)
{
struct
cp210x_port_private
*
port_priv
=
usb_get_serial_port_data
(
port
);
u16
line_ctl_save
;
u16
line_ctl_test
;
int
err
;
err
=
cp210x_read_u16_reg
(
port
,
CP210X_GET_LINE_CTL
,
&
line_ctl_save
);
if
(
err
)
return
err
;
err
=
cp210x_write_u16_reg
(
port
,
CP210X_SET_LINE_CTL
,
0x800
);
if
(
err
)
return
err
;
result
=
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
err
=
cp210x_read_u16_reg
(
port
,
CP210X_GET_LINE_CTL
,
&
line_ctl_test
);
CP210X_IFC_ENABLE
,
UART_ENABLE
,
NULL
,
0
);
if
(
err
)
return
err
;
if
(
line_ctl_test
==
8
)
{
port_priv
->
has_swapped_line_ctl
=
true
;
line_ctl_save
=
swab16
(
line_ctl_save
);
}
return
cp210x_write_u16_reg
(
port
,
CP210X_SET_LINE_CTL
,
line_ctl_save
);
}
/*
* Must always be called instead of cp210x_read_u16_reg(CP210X_GET_LINE_CTL)
* to workaround cp2108 bug and get correct value.
*/
static
int
cp210x_get_line_ctl
(
struct
usb_serial_port
*
port
,
u16
*
ctl
)
{
struct
cp210x_port_private
*
port_priv
=
usb_get_serial_port_data
(
port
);
int
err
;
err
=
cp210x_read_u16_reg
(
port
,
CP210X_GET_LINE_CTL
,
ctl
);
if
(
err
)
return
err
;
/* Workaround swapped bytes in 16-bit value from CP210X_GET_LINE_CTL */
if
(
port_priv
->
has_swapped_line_ctl
)
*
ctl
=
swab16
(
*
ctl
);
return
0
;
}
static
int
cp210x_open
(
struct
tty_struct
*
tty
,
struct
usb_serial_port
*
port
)
{
int
result
;
result
=
cp210x_write_u16_reg
(
port
,
CP210X_IFC_ENABLE
,
UART_ENABLE
);
if
(
result
)
{
if
(
result
)
{
dev_err
(
&
port
->
dev
,
"%s - Unable to enable UART
\n
"
,
__func__
);
dev_err
(
&
port
->
dev
,
"%s - Unable to enable UART
\n
"
,
__func__
);
return
result
;
return
result
;
...
@@ -453,80 +826,57 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
...
@@ -453,80 +826,57 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
static
void
cp210x_close
(
struct
usb_serial_port
*
port
)
static
void
cp210x_close
(
struct
usb_serial_port
*
port
)
{
{
dbg
(
"%s - port %d"
,
__func__
,
port
->
number
);
usb_serial_generic_close
(
port
);
usb_serial_generic_close
(
port
);
mutex_lock
(
&
port
->
serial
->
disc_mutex
);
/* Clear both queues; cp2108 needs this to avoid an occasional hang */
if
(
!
port
->
serial
->
disconnected
)
cp210x_write_u16_reg
(
port
,
CP210X_PURGE
,
PURGE_ALL
);
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
CP210X_IFC_ENABLE
,
UART_DISABLE
,
NULL
,
0
);
cp210x_write_u16_reg
(
port
,
CP210X_IFC_ENABLE
,
UART_DISABLE
);
mutex_unlock
(
&
port
->
serial
->
disc_mutex
);
}
}
static
int
cp210x_ioctl
(
struct
tty_struct
*
tty
,
/*
unsigned
int
cmd
,
unsigned
long
arg
)
* Read how many bytes are waiting in the TX queue.
*/
static
int
cp210x_get_tx_queue_byte_count
(
struct
usb_serial_port
*
port
,
u32
*
count
)
{
{
struct
usb_serial
_port
*
port
=
tty
->
driver_data
;
struct
usb_serial
*
serial
=
port
->
serial
;
struct
cp210x_port_private
*
port_priv
=
usb_get_serial_port_data
(
port
);
struct
cp210x_port_private
*
port_priv
=
usb_get_serial_port_data
(
port
);
struct
cp210x_comm_status
*
sts
;
int
result
;
printk
(
"port %x priv %x cmd %x arg %x %x
\n
"
,
port
,
port_priv
,
cmd
,
arg
,
*
(
unsigned
char
*
)
arg
);
sts
=
kmalloc
(
sizeof
(
*
sts
),
GFP_KERNEL
);
if
(
arg
==
0
)
if
(
!
sts
)
return
-
EINVAL
;
return
-
ENOMEM
;
switch
(
cmd
)
{
result
=
usb_control_msg
(
serial
->
dev
,
usb_rcvctrlpipe
(
serial
->
dev
,
0
),
CP210X_GET_COMM_STATUS
,
REQTYPE_INTERFACE_TO_HOST
,
0
,
port_priv
->
bInterfaceNumber
,
sts
,
sizeof
(
*
sts
),
USB_CTRL_GET_TIMEOUT
);
if
(
result
==
sizeof
(
*
sts
))
{
*
count
=
le32_to_cpu
(
sts
->
ulAmountInOutQueue
);
result
=
0
;
}
else
{
dev_err
(
&
port
->
dev
,
"failed to get comm status: %d
\n
"
,
result
);
if
(
result
>=
0
)
result
=
-
EIO
;
}
case
IOCTL_GPIOGET
:
kfree
(
sts
);
if
((
port_priv
->
bPartNumber
==
CP2103_PARTNUM
)
||
(
port_priv
->
bPartNumber
==
CP2104_PARTNUM
))
{
cp210x_get_config
(
port
,
REQTYPE_DEVICE_TO_HOST
,
CP210X_VENDOR_SPECIFIC
,
CP210X_READ_LATCH
,
(
unsigned
int
*
)
arg
,
1
);
}
else
if
(
port_priv
->
bPartNumber
==
CP2105_PARTNUM
)
{
cp210x_get_config
(
port
,
REQTYPE_INTERFACE_TO_HOST
,
CP210X_VENDOR_SPECIFIC
,
CP210X_READ_LATCH
,
(
unsigned
int
*
)
arg
,
1
);
}
else
{
return
-
ENOTSUPP
;
}
break
;
case
IOCTL_GPIOSET
:
return
result
;
if
((
port_priv
->
bPartNumber
==
CP2103_PARTNUM
)
||
}
(
port_priv
->
bPartNumber
==
CP2104_PARTNUM
))
{
/*cp210x_set_config(port, REQTYPE_HOST_TO_DEVICE,
CP210x_VENDOR_SPECIFIC,
CP210x_GPIO_WRITE_LATCH, &val, 2);*/
// printk("dev %x arg %x\n", port->serial->dev, arg);
usb_control_msg
(
port
->
serial
->
dev
,
usb_sndctrlpipe
(
port
->
serial
->
dev
,
0
),
CP210X_VENDOR_SPECIFIC
,
REQTYPE_HOST_TO_DEVICE
,
CP210X_WRITE_LATCH
,
*
(
unsigned
long
*
)
arg
,
NULL
,
0
,
300
);
}
else
if
(
port_priv
->
bPartNumber
==
CP2105_PARTNUM
)
{
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
CP210X_VENDOR_SPECIFIC
,
CP210X_WRITE_LATCH
,
(
unsigned
int
*
)
arg
,
2
);
}
else
{
return
-
ENOTSUPP
;
}
break
;
default:
static
bool
cp210x_tx_empty
(
struct
usb_serial_port
*
port
)
return
-
ENOIOCTLCMD
;
{
break
;
int
err
;
}
u32
count
;
return
0
;
err
=
cp210x_get_tx_queue_byte_count
(
port
,
&
count
);
if
(
err
)
return
true
;
return
!
count
;
}
}
/*
/*
...
@@ -542,12 +892,10 @@ static void cp210x_get_termios(struct tty_struct *tty,
...
@@ -542,12 +892,10 @@ static void cp210x_get_termios(struct tty_struct *tty,
if
(
tty
)
{
if
(
tty
)
{
cp210x_get_termios_port
(
tty
->
driver_data
,
cp210x_get_termios_port
(
tty
->
driver_data
,
&
tty
->
termios
->
c_cflag
,
&
baud
);
&
tty
->
termios
.
c_cflag
,
&
baud
);
tty_encode_baud_rate
(
tty
,
baud
,
baud
);
tty_encode_baud_rate
(
tty
,
baud
,
baud
);
}
}
else
{
tcflag_t
cflag
;
else
{
unsigned
int
cflag
;
cflag
=
0
;
cflag
=
0
;
cp210x_get_termios_port
(
port
,
&
cflag
,
&
baud
);
cp210x_get_termios_port
(
port
,
&
cflag
,
&
baud
);
}
}
...
@@ -558,131 +906,189 @@ static void cp210x_get_termios(struct tty_struct *tty,
...
@@ -558,131 +906,189 @@ static void cp210x_get_termios(struct tty_struct *tty,
* This is the heart of cp210x_get_termios which always uses a &usb_serial_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
,
static
void
cp210x_get_termios_port
(
struct
usb_serial_port
*
port
,
unsigned
in
t
*
cflagp
,
unsigned
int
*
baudp
)
tcflag_
t
*
cflagp
,
unsigned
int
*
baudp
)
{
{
unsigned
int
cflag
,
modem_ctl
[
4
];
struct
device
*
dev
=
&
port
->
dev
;
unsigned
int
baud
;
tcflag_t
cflag
;
unsigned
int
bits
;
struct
cp210x_flow_ctl
flow_ctl
;
u32
baud
;
dbg
(
"%s - port %d"
,
__func__
,
port
->
number
);
u16
bits
;
u32
ctl_hs
;
cp210x_get_config
(
port
,
REQTYPE_INTERFACE_TO_HOST
,
cp210x_read_u32_reg
(
port
,
CP210X_GET_BAUDRATE
,
&
baud
);
CP210X_GET_BAUDRATE
,
0
,
&
baud
,
4
);
d
bg
(
"%s - baud rate = %d
"
,
__func__
,
baud
);
d
ev_dbg
(
dev
,
"%s - baud rate = %d
\n
"
,
__func__
,
baud
);
*
baudp
=
baud
;
*
baudp
=
baud
;
cflag
=
*
cflagp
;
cflag
=
*
cflagp
;
cp210x_get_config
(
port
,
REQTYPE_INTERFACE_TO_HOST
,
cp210x_get_line_ctl
(
port
,
&
bits
);
CP210X_GET_LINE_CTL
,
0
,
&
bits
,
2
);
cflag
&=
~
CSIZE
;
cflag
&=
~
CSIZE
;
switch
(
bits
&
BITS_DATA_MASK
)
{
switch
(
bits
&
BITS_DATA_MASK
)
{
case
BITS_DATA_5
:
case
BITS_DATA_5
:
d
bg
(
"%s - data bits = 5
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - data bits = 5
\n
"
,
__func__
);
cflag
|=
CS5
;
cflag
|=
CS5
;
break
;
break
;
case
BITS_DATA_6
:
case
BITS_DATA_6
:
d
bg
(
"%s - data bits = 6
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - data bits = 6
\n
"
,
__func__
);
cflag
|=
CS6
;
cflag
|=
CS6
;
break
;
break
;
case
BITS_DATA_7
:
case
BITS_DATA_7
:
d
bg
(
"%s - data bits = 7
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - data bits = 7
\n
"
,
__func__
);
cflag
|=
CS7
;
cflag
|=
CS7
;
break
;
break
;
case
BITS_DATA_8
:
case
BITS_DATA_8
:
d
bg
(
"%s - data bits = 8
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - data bits = 8
\n
"
,
__func__
);
cflag
|=
CS8
;
cflag
|=
CS8
;
break
;
break
;
case
BITS_DATA_9
:
case
BITS_DATA_9
:
dbg
(
"%s - data bits = 9 (not supported, using 8 data bits)"
,
dev_dbg
(
dev
,
"%s - data bits = 9 (not supported, using 8 data bits)
\n
"
,
__func__
);
__func__
);
cflag
|=
CS8
;
cflag
|=
CS8
;
bits
&=
~
BITS_DATA_MASK
;
bits
&=
~
BITS_DATA_MASK
;
bits
|=
BITS_DATA_8
;
bits
|=
BITS_DATA_8
;
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
cp210x_write_u16_reg
(
port
,
CP210X_SET_LINE_CTL
,
bits
);
CP210X_SET_LINE_CTL
,
0
,
&
bits
,
2
);
break
;
break
;
default:
default:
d
bg
(
"%s - Unknown number of data bits, using 8
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - Unknown number of data bits, using 8
\n
"
,
__func__
);
cflag
|=
CS8
;
cflag
|=
CS8
;
bits
&=
~
BITS_DATA_MASK
;
bits
&=
~
BITS_DATA_MASK
;
bits
|=
BITS_DATA_8
;
bits
|=
BITS_DATA_8
;
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
cp210x_write_u16_reg
(
port
,
CP210X_SET_LINE_CTL
,
bits
);
CP210X_SET_LINE_CTL
,
0
,
&
bits
,
2
);
break
;
break
;
}
}
switch
(
bits
&
BITS_PARITY_MASK
)
{
switch
(
bits
&
BITS_PARITY_MASK
)
{
case
BITS_PARITY_NONE
:
case
BITS_PARITY_NONE
:
d
bg
(
"%s - parity = NONE
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - parity = NONE
\n
"
,
__func__
);
cflag
&=
~
PARENB
;
cflag
&=
~
PARENB
;
break
;
break
;
case
BITS_PARITY_ODD
:
case
BITS_PARITY_ODD
:
d
bg
(
"%s - parity = ODD
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - parity = ODD
\n
"
,
__func__
);
cflag
|=
(
PARENB
|
PARODD
);
cflag
|=
(
PARENB
|
PARODD
);
break
;
break
;
case
BITS_PARITY_EVEN
:
case
BITS_PARITY_EVEN
:
d
bg
(
"%s - parity = EVEN
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - parity = EVEN
\n
"
,
__func__
);
cflag
&=
~
PARODD
;
cflag
&=
~
PARODD
;
cflag
|=
PARENB
;
cflag
|=
PARENB
;
break
;
break
;
case
BITS_PARITY_MARK
:
case
BITS_PARITY_MARK
:
d
bg
(
"%s - parity = MARK
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - parity = MARK
\n
"
,
__func__
);
cflag
|=
(
PARENB
|
PARODD
|
CMSPAR
);
cflag
|=
(
PARENB
|
PARODD
|
CMSPAR
);
break
;
break
;
case
BITS_PARITY_SPACE
:
case
BITS_PARITY_SPACE
:
d
bg
(
"%s - parity = SPACE
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - parity = SPACE
\n
"
,
__func__
);
cflag
&=
~
PARODD
;
cflag
&=
~
PARODD
;
cflag
|=
(
PARENB
|
CMSPAR
);
cflag
|=
(
PARENB
|
CMSPAR
);
break
;
break
;
default:
default:
d
bg
(
"%s - Unknown parity mode, disabling parity
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - Unknown parity mode, disabling parity
\n
"
,
__func__
);
cflag
&=
~
PARENB
;
cflag
&=
~
PARENB
;
bits
&=
~
BITS_PARITY_MASK
;
bits
&=
~
BITS_PARITY_MASK
;
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
cp210x_write_u16_reg
(
port
,
CP210X_SET_LINE_CTL
,
bits
);
CP210X_SET_LINE_CTL
,
0
,
&
bits
,
2
);
break
;
break
;
}
}
cflag
&=
~
CSTOPB
;
cflag
&=
~
CSTOPB
;
switch
(
bits
&
BITS_STOP_MASK
)
{
switch
(
bits
&
BITS_STOP_MASK
)
{
case
BITS_STOP_1
:
case
BITS_STOP_1
:
d
bg
(
"%s - stop bits = 1
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - stop bits = 1
\n
"
,
__func__
);
break
;
break
;
case
BITS_STOP_1_5
:
case
BITS_STOP_1_5
:
dbg
(
"%s - stop bits = 1.5 (not supported, using 1 stop bit)"
,
dev_dbg
(
dev
,
"%s - stop bits = 1.5 (not supported, using 1 stop bit)
\n
"
,
__func__
);
__func__
);
bits
&=
~
BITS_STOP_MASK
;
bits
&=
~
BITS_STOP_MASK
;
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
cp210x_write_u16_reg
(
port
,
CP210X_SET_LINE_CTL
,
bits
);
CP210X_SET_LINE_CTL
,
0
,
&
bits
,
2
);
break
;
break
;
case
BITS_STOP_2
:
case
BITS_STOP_2
:
d
bg
(
"%s - stop bits = 2
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - stop bits = 2
\n
"
,
__func__
);
cflag
|=
CSTOPB
;
cflag
|=
CSTOPB
;
break
;
break
;
default:
default:
dbg
(
"%s - Unknown number of stop bits, using 1 stop bit"
,
dev_dbg
(
dev
,
"%s - Unknown number of stop bits, using 1 stop bit
\n
"
,
__func__
);
__func__
);
bits
&=
~
BITS_STOP_MASK
;
bits
&=
~
BITS_STOP_MASK
;
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
cp210x_write_u16_reg
(
port
,
CP210X_SET_LINE_CTL
,
bits
);
CP210X_SET_LINE_CTL
,
0
,
&
bits
,
2
);
break
;
break
;
}
}
cp210x_get_config
(
port
,
REQTYPE_INTERFACE_TO_HOST
,
cp210x_read_reg_block
(
port
,
CP210X_GET_FLOW
,
&
flow_ctl
,
CP210X_GET_FLOW
,
0
,
modem_ctl
,
16
);
sizeof
(
flow_ctl
));
if
(
modem_ctl
[
0
]
&
0x0008
)
{
ctl_hs
=
le32_to_cpu
(
flow_ctl
.
ulControlHandshake
);
dbg
(
"%s - flow control = CRTSCTS"
,
__func__
);
if
(
ctl_hs
&
CP210X_SERIAL_CTS_HANDSHAKE
)
{
dev_dbg
(
dev
,
"%s - flow control = CRTSCTS
\n
"
,
__func__
);
cflag
|=
CRTSCTS
;
cflag
|=
CRTSCTS
;
}
else
{
}
else
{
d
bg
(
"%s - flow control = NONE
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - flow control = NONE
\n
"
,
__func__
);
cflag
&=
~
CRTSCTS
;
cflag
&=
~
CRTSCTS
;
}
}
*
cflagp
=
cflag
;
*
cflagp
=
cflag
;
}
}
struct
cp210x_rate
{
speed_t
rate
;
speed_t
high
;
};
static
const
struct
cp210x_rate
cp210x_an205_table1
[]
=
{
{
300
,
300
},
{
600
,
600
},
{
1200
,
1200
},
{
1800
,
1800
},
{
2400
,
2400
},
{
4000
,
4000
},
{
4800
,
4803
},
{
7200
,
7207
},
{
9600
,
9612
},
{
14400
,
14428
},
{
16000
,
16062
},
{
19200
,
19250
},
{
28800
,
28912
},
{
38400
,
38601
},
{
51200
,
51558
},
{
56000
,
56280
},
{
57600
,
58053
},
{
64000
,
64111
},
{
76800
,
77608
},
{
115200
,
117028
},
{
128000
,
129347
},
{
153600
,
156868
},
{
230400
,
237832
},
{
250000
,
254234
},
{
256000
,
273066
},
{
460800
,
491520
},
{
500000
,
567138
},
{
576000
,
670254
},
{
921600
,
UINT_MAX
}
};
/*
* Quantises the baud rate as per AN205 Table 1
*/
static
speed_t
cp210x_get_an205_rate
(
speed_t
baud
)
{
int
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
cp210x_an205_table1
);
++
i
)
{
if
(
baud
<=
cp210x_an205_table1
[
i
].
high
)
break
;
}
return
cp210x_an205_table1
[
i
].
rate
;
}
static
speed_t
cp210x_get_actual_rate
(
speed_t
baud
)
{
unsigned
int
prescale
=
1
;
unsigned
int
div
;
if
(
baud
<=
365
)
prescale
=
4
;
div
=
DIV_ROUND_CLOSEST
(
48000000
,
2
*
prescale
*
baud
);
baud
=
48000000
/
(
2
*
prescale
*
div
);
return
baud
;
}
/*
/*
* CP2101 supports the following baud rates:
* CP2101 supports the following baud rates:
*
*
...
@@ -712,20 +1118,25 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
...
@@ -712,20 +1118,25 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
static
void
cp210x_change_speed
(
struct
tty_struct
*
tty
,
static
void
cp210x_change_speed
(
struct
tty_struct
*
tty
,
struct
usb_serial_port
*
port
,
struct
ktermios
*
old_termios
)
struct
usb_serial_port
*
port
,
struct
ktermios
*
old_termios
)
{
{
struct
usb_serial
*
serial
=
port
->
serial
;
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
u32
baud
;
u32
baud
;
baud
=
tty
->
termios
->
c_ospeed
;
/*
* This maps the requested rate to the actual rate, a valid rate on
/* This maps the requested rate to a rate valid on cp2102 or cp2103,
* cp2102 or cp2103, or to an arbitrary rate in [1M, max_speed].
* or to an arbitrary rate in [1M,2M].
*
*
* NOTE: B0 is not implemented.
* NOTE: B0 is not implemented.
*/
*/
baud
=
cp210x_quantise_baudrate
(
baud
);
baud
=
clamp
(
tty
->
termios
.
c_ospeed
,
priv
->
min_speed
,
priv
->
max_speed
);
if
(
priv
->
use_actual_rate
)
baud
=
cp210x_get_actual_rate
(
baud
);
else
if
(
baud
<
1000000
)
baud
=
cp210x_get_an205_rate
(
baud
);
dbg
(
"%s - setting baud rate to %u"
,
__func__
,
baud
);
dev_dbg
(
&
port
->
dev
,
"%s - setting baud rate to %u
\n
"
,
__func__
,
baud
);
if
(
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
if
(
cp210x_write_u32_reg
(
port
,
CP210X_SET_BAUDRATE
,
baud
))
{
CP210X_SET_BAUDRATE
,
0
,
&
baud
,
sizeof
(
baud
)))
{
dev_warn
(
&
port
->
dev
,
"failed to set baud rate to %u
\n
"
,
baud
);
dev_warn
(
&
port
->
dev
,
"failed to set baud rate to %u
\n
"
,
baud
);
if
(
old_termios
)
if
(
old_termios
)
baud
=
old_termios
->
c_ospeed
;
baud
=
old_termios
->
c_ospeed
;
...
@@ -739,135 +1150,128 @@ static void cp210x_change_speed(struct tty_struct *tty,
...
@@ -739,135 +1150,128 @@ static void cp210x_change_speed(struct tty_struct *tty,
static
void
cp210x_set_termios
(
struct
tty_struct
*
tty
,
static
void
cp210x_set_termios
(
struct
tty_struct
*
tty
,
struct
usb_serial_port
*
port
,
struct
ktermios
*
old_termios
)
struct
usb_serial_port
*
port
,
struct
ktermios
*
old_termios
)
{
{
struct
device
*
dev
=
&
port
->
dev
;
unsigned
int
cflag
,
old_cflag
;
unsigned
int
cflag
,
old_cflag
;
unsigned
int
bits
;
u16
bits
;
unsigned
int
modem_ctl
[
4
];
dbg
(
"%s - port %d"
,
__func__
,
port
->
number
);
if
(
!
tty
)
return
;
cflag
=
tty
->
termios
->
c_cflag
;
cflag
=
tty
->
termios
.
c_cflag
;
old_cflag
=
old_termios
->
c_cflag
;
old_cflag
=
old_termios
->
c_cflag
;
cp210x_change_speed
(
tty
,
port
,
old_termios
);
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 the number of data bits is to be updated */
if
((
cflag
&
CSIZE
)
!=
(
old_cflag
&
CSIZE
))
{
if
((
cflag
&
CSIZE
)
!=
(
old_cflag
&
CSIZE
))
{
cp210x_get_config
(
port
,
REQTYPE_INTERFACE_TO_HOST
,
cp210x_get_line_ctl
(
port
,
&
bits
);
CP210X_GET_LINE_CTL
,
0
,
&
bits
,
2
);
bits
&=
~
BITS_DATA_MASK
;
bits
&=
~
BITS_DATA_MASK
;
switch
(
cflag
&
CSIZE
)
{
switch
(
cflag
&
CSIZE
)
{
case
CS5
:
case
CS5
:
bits
|=
BITS_DATA_5
;
bits
|=
BITS_DATA_5
;
d
bg
(
"%s - data bits = 5
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - data bits = 5
\n
"
,
__func__
);
break
;
break
;
case
CS6
:
case
CS6
:
bits
|=
BITS_DATA_6
;
bits
|=
BITS_DATA_6
;
d
bg
(
"%s - data bits = 6
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - data bits = 6
\n
"
,
__func__
);
break
;
break
;
case
CS7
:
case
CS7
:
bits
|=
BITS_DATA_7
;
bits
|=
BITS_DATA_7
;
d
bg
(
"%s - data bits = 7
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - data bits = 7
\n
"
,
__func__
);
break
;
break
;
case
CS8
:
case
CS8
:
default:
bits
|=
BITS_DATA_8
;
bits
|=
BITS_DATA_8
;
d
bg
(
"%s - data bits = 8
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - data bits = 8
\n
"
,
__func__
);
break
;
break
;
/*case CS9:
bits |= BITS_DATA_9;
dbg("%s - data bits = 9", __func__);
break;*/
default:
dbg
(
"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
,
REQTYPE_HOST_TO_INTERFACE
,
if
(
cp210x_write_u16_reg
(
port
,
CP210X_SET_LINE_CTL
,
bits
))
CP210X_SET_LINE_CTL
,
0
,
&
bits
,
2
))
dev_dbg
(
dev
,
"Number of data bits requested not supported by device
\n
"
);
dbg
(
"Number of data bits requested "
"not supported by device
\n
"
);
}
}
if
((
cflag
&
(
PARENB
|
PARODD
|
CMSPAR
))
!=
if
((
cflag
&
(
PARENB
|
PARODD
|
CMSPAR
))
!=
(
old_cflag
&
(
PARENB
|
PARODD
|
CMSPAR
)))
{
(
old_cflag
&
(
PARENB
|
PARODD
|
CMSPAR
)))
{
cp210x_get_config
(
port
,
REQTYPE_INTERFACE_TO_HOST
,
cp210x_get_line_ctl
(
port
,
&
bits
);
CP210X_GET_LINE_CTL
,
0
,
&
bits
,
2
);
bits
&=
~
BITS_PARITY_MASK
;
bits
&=
~
BITS_PARITY_MASK
;
if
(
cflag
&
PARENB
)
{
if
(
cflag
&
PARENB
)
{
if
(
cflag
&
CMSPAR
)
{
if
(
cflag
&
CMSPAR
)
{
if
(
cflag
&
PARODD
)
{
if
(
cflag
&
PARODD
)
{
bits
|=
BITS_PARITY_MARK
;
bits
|=
BITS_PARITY_MARK
;
dbg
(
"%s - parity = MARK
"
,
__func__
);
dev_dbg
(
dev
,
"%s - parity = MARK
\n
"
,
__func__
);
}
else
{
}
else
{
bits
|=
BITS_PARITY_SPACE
;
bits
|=
BITS_PARITY_SPACE
;
dbg
(
"%s - parity = SPACE
"
,
__func__
);
dev_dbg
(
dev
,
"%s - parity = SPACE
\n
"
,
__func__
);
}
}
}
else
{
}
else
{
if
(
cflag
&
PARODD
)
{
if
(
cflag
&
PARODD
)
{
bits
|=
BITS_PARITY_ODD
;
bits
|=
BITS_PARITY_ODD
;
dbg
(
"%s - parity = ODD
"
,
__func__
);
dev_dbg
(
dev
,
"%s - parity = ODD
\n
"
,
__func__
);
}
else
{
}
else
{
bits
|=
BITS_PARITY_EVEN
;
bits
|=
BITS_PARITY_EVEN
;
dbg
(
"%s - parity = EVEN
"
,
__func__
);
dev_dbg
(
dev
,
"%s - parity = EVEN
\n
"
,
__func__
);
}
}
}
}
}
}
if
(
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
if
(
cp210x_write_u16_reg
(
port
,
CP210X_SET_LINE_CTL
,
bits
))
CP210X_SET_LINE_CTL
,
0
,
&
bits
,
2
))
dev_dbg
(
dev
,
"Parity mode not supported by device
\n
"
);
dbg
(
"Parity mode not supported "
"by device
\n
"
);
}
}
if
((
cflag
&
CSTOPB
)
!=
(
old_cflag
&
CSTOPB
))
{
if
((
cflag
&
CSTOPB
)
!=
(
old_cflag
&
CSTOPB
))
{
cp210x_get_config
(
port
,
REQTYPE_INTERFACE_TO_HOST
,
cp210x_get_line_ctl
(
port
,
&
bits
);
CP210X_GET_LINE_CTL
,
0
,
&
bits
,
2
);
bits
&=
~
BITS_STOP_MASK
;
bits
&=
~
BITS_STOP_MASK
;
if
(
cflag
&
CSTOPB
)
{
if
(
cflag
&
CSTOPB
)
{
bits
|=
BITS_STOP_2
;
bits
|=
BITS_STOP_2
;
d
bg
(
"%s - stop bits = 2
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - stop bits = 2
\n
"
,
__func__
);
}
else
{
}
else
{
bits
|=
BITS_STOP_1
;
bits
|=
BITS_STOP_1
;
d
bg
(
"%s - stop bits = 1
"
,
__func__
);
d
ev_dbg
(
dev
,
"%s - stop bits = 1
\n
"
,
__func__
);
}
}
if
(
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
if
(
cp210x_write_u16_reg
(
port
,
CP210X_SET_LINE_CTL
,
bits
))
CP210X_SET_LINE_CTL
,
0
,
&
bits
,
2
))
dev_dbg
(
dev
,
"Number of stop bits requested not supported by device
\n
"
);
dbg
(
"Number of stop bits requested "
"not supported by device
\n
"
);
}
}
if
((
cflag
&
CRTSCTS
)
!=
(
old_cflag
&
CRTSCTS
))
{
if
((
cflag
&
CRTSCTS
)
!=
(
old_cflag
&
CRTSCTS
))
{
cp210x_get_config
(
port
,
REQTYPE_INTERFACE_TO_HOST
,
struct
cp210x_flow_ctl
flow_ctl
;
CP210X_GET_FLOW
,
0
,
modem_ctl
,
16
);
u32
ctl_hs
;
dbg
(
"%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x"
,
u32
flow_repl
;
__func__
,
modem_ctl
[
0
],
modem_ctl
[
1
],
modem_ctl
[
2
],
modem_ctl
[
3
]);
cp210x_read_reg_block
(
port
,
CP210X_GET_FLOW
,
&
flow_ctl
,
sizeof
(
flow_ctl
));
ctl_hs
=
le32_to_cpu
(
flow_ctl
.
ulControlHandshake
);
flow_repl
=
le32_to_cpu
(
flow_ctl
.
ulFlowReplace
);
dev_dbg
(
dev
,
"%s - read ulControlHandshake=0x%08x, ulFlowReplace=0x%08x
\n
"
,
__func__
,
ctl_hs
,
flow_repl
);
ctl_hs
&=
~
CP210X_SERIAL_DSR_HANDSHAKE
;
ctl_hs
&=
~
CP210X_SERIAL_DCD_HANDSHAKE
;
ctl_hs
&=
~
CP210X_SERIAL_DSR_SENSITIVITY
;
ctl_hs
&=
~
CP210X_SERIAL_DTR_MASK
;
ctl_hs
|=
CP210X_SERIAL_DTR_SHIFT
(
CP210X_SERIAL_DTR_ACTIVE
);
if
(
cflag
&
CRTSCTS
)
{
if
(
cflag
&
CRTSCTS
)
{
modem_ctl
[
0
]
&=
~
0x7B
;
ctl_hs
|=
CP210X_SERIAL_CTS_HANDSHAKE
;
modem_ctl
[
0
]
|=
0x09
;
modem_ctl
[
1
]
=
0x80
;
flow_repl
&=
~
CP210X_SERIAL_RTS_MASK
;
dbg
(
"%s - flow control = CRTSCTS"
,
__func__
);
flow_repl
|=
CP210X_SERIAL_RTS_SHIFT
(
CP210X_SERIAL_RTS_FLOW_CTL
);
dev_dbg
(
dev
,
"%s - flow control = CRTSCTS
\n
"
,
__func__
);
}
else
{
}
else
{
modem_ctl
[
0
]
&=
~
0x7B
;
ctl_hs
&=
~
CP210X_SERIAL_CTS_HANDSHAKE
;
modem_ctl
[
0
]
|=
0x01
;
modem_ctl
[
1
]
|=
0x40
;
flow_repl
&=
~
CP210X_SERIAL_RTS_MASK
;
dbg
(
"%s - flow control = NONE"
,
__func__
);
flow_repl
|=
CP210X_SERIAL_RTS_SHIFT
(
CP210X_SERIAL_RTS_ACTIVE
);
dev_dbg
(
dev
,
"%s - flow control = NONE
\n
"
,
__func__
);
}
}
dbg
(
"%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x"
,
dev_dbg
(
dev
,
"%s - write ulControlHandshake=0x%08x, ulFlowReplace=0x%08x
\n
"
,
__func__
,
modem_ctl
[
0
],
modem_ctl
[
1
],
__func__
,
ctl_hs
,
flow_repl
);
modem_ctl
[
2
],
modem_ctl
[
3
]);
flow_ctl
.
ulControlHandshake
=
cpu_to_le32
(
ctl_hs
);
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
flow_ctl
.
ulFlowReplace
=
cpu_to_le32
(
flow_repl
);
CP210X_SET_FLOW
,
0
,
modem_ctl
,
16
);
cp210x_write_reg_block
(
port
,
CP210X_SET_FLOW
,
&
flow_ctl
,
sizeof
(
flow_ctl
));
}
}
}
}
static
int
cp210x_tiocmset
(
struct
tty_struct
*
tty
,
static
int
cp210x_tiocmset
(
struct
tty_struct
*
tty
,
unsigned
int
set
,
unsigned
int
clear
)
unsigned
int
set
,
unsigned
int
clear
)
{
{
struct
usb_serial_port
*
port
=
tty
->
driver_data
;
struct
usb_serial_port
*
port
=
tty
->
driver_data
;
...
@@ -877,9 +1281,7 @@ static int cp210x_tiocmset (struct tty_struct *tty,
...
@@ -877,9 +1281,7 @@ static int cp210x_tiocmset (struct tty_struct *tty,
static
int
cp210x_tiocmset_port
(
struct
usb_serial_port
*
port
,
static
int
cp210x_tiocmset_port
(
struct
usb_serial_port
*
port
,
unsigned
int
set
,
unsigned
int
clear
)
unsigned
int
set
,
unsigned
int
clear
)
{
{
unsigned
int
control
=
0
;
u16
control
=
0
;
dbg
(
"%s - port %d"
,
__func__
,
port
->
number
);
if
(
set
&
TIOCM_RTS
)
{
if
(
set
&
TIOCM_RTS
)
{
control
|=
CONTROL_RTS
;
control
|=
CONTROL_RTS
;
...
@@ -898,10 +1300,9 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port,
...
@@ -898,10 +1300,9 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port,
control
|=
CONTROL_WRITE_DTR
;
control
|=
CONTROL_WRITE_DTR
;
}
}
d
bg
(
"%s - control = 0x%.4x
"
,
__func__
,
control
);
d
ev_dbg
(
&
port
->
dev
,
"%s - control = 0x%.4x
\n
"
,
__func__
,
control
);
return
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
return
cp210x_write_u16_reg
(
port
,
CP210X_SET_MHS
,
control
);
CP210X_SET_MHS
,
0
,
&
control
,
2
);
}
}
static
void
cp210x_dtr_rts
(
struct
usb_serial_port
*
p
,
int
on
)
static
void
cp210x_dtr_rts
(
struct
usb_serial_port
*
p
,
int
on
)
...
@@ -912,16 +1313,15 @@ static void cp210x_dtr_rts(struct usb_serial_port *p, int on)
...
@@ -912,16 +1313,15 @@ static void cp210x_dtr_rts(struct usb_serial_port *p, int on)
cp210x_tiocmset_port
(
p
,
0
,
TIOCM_DTR
|
TIOCM_RTS
);
cp210x_tiocmset_port
(
p
,
0
,
TIOCM_DTR
|
TIOCM_RTS
);
}
}
static
int
cp210x_tiocmget
(
struct
tty_struct
*
tty
)
static
int
cp210x_tiocmget
(
struct
tty_struct
*
tty
)
{
{
struct
usb_serial_port
*
port
=
tty
->
driver_data
;
struct
usb_serial_port
*
port
=
tty
->
driver_data
;
u
nsigned
int
control
;
u
8
control
;
int
result
;
int
result
;
dbg
(
"%s - port %d"
,
__func__
,
port
->
number
);
result
=
cp210x_read_u8_reg
(
port
,
CP210X_GET_MDMSTS
,
&
control
);
if
(
result
)
cp210x_get_config
(
port
,
REQTYPE_INTERFACE_TO_HOST
,
return
result
;
CP210X_GET_MDMSTS
,
0
,
&
control
,
1
);
result
=
((
control
&
CONTROL_DTR
)
?
TIOCM_DTR
:
0
)
result
=
((
control
&
CONTROL_DTR
)
?
TIOCM_DTR
:
0
)
|
((
control
&
CONTROL_RTS
)
?
TIOCM_RTS
:
0
)
|
((
control
&
CONTROL_RTS
)
?
TIOCM_RTS
:
0
)
...
@@ -930,103 +1330,571 @@ static int cp210x_tiocmget (struct tty_struct *tty)
...
@@ -930,103 +1330,571 @@ static int cp210x_tiocmget (struct tty_struct *tty)
|
((
control
&
CONTROL_RING
)
?
TIOCM_RI
:
0
)
|
((
control
&
CONTROL_RING
)
?
TIOCM_RI
:
0
)
|
((
control
&
CONTROL_DCD
)
?
TIOCM_CD
:
0
);
|
((
control
&
CONTROL_DCD
)
?
TIOCM_CD
:
0
);
d
bg
(
"%s - control = 0x%.2x
"
,
__func__
,
control
);
d
ev_dbg
(
&
port
->
dev
,
"%s - control = 0x%.2x
\n
"
,
__func__
,
control
);
return
result
;
return
result
;
}
}
static
void
cp210x_break_ctl
(
struct
tty_struct
*
tty
,
int
break_state
)
static
void
cp210x_break_ctl
(
struct
tty_struct
*
tty
,
int
break_state
)
{
{
struct
usb_serial_port
*
port
=
tty
->
driver_data
;
struct
usb_serial_port
*
port
=
tty
->
driver_data
;
u
nsigned
int
state
;
u
16
state
;
dbg
(
"%s - port %d"
,
__func__
,
port
->
number
);
if
(
break_state
==
0
)
if
(
break_state
==
0
)
state
=
BREAK_OFF
;
state
=
BREAK_OFF
;
else
else
state
=
BREAK_ON
;
state
=
BREAK_ON
;
dbg
(
"%s - turning break %s"
,
__func__
,
dev_dbg
(
&
port
->
dev
,
"%s - turning break %s
\n
"
,
__func__
,
state
==
BREAK_OFF
?
"off"
:
"on"
);
state
==
BREAK_OFF
?
"off"
:
"on"
);
cp210x_set_config
(
port
,
REQTYPE_HOST_TO_INTERFACE
,
cp210x_write_u16_reg
(
port
,
CP210X_SET_BREAK
,
state
);
CP210X_SET_BREAK
,
0
,
&
state
,
2
);
}
}
static
int
cp210x_startup
(
struct
usb_serial
*
serial
)
#ifdef CONFIG_GPIOLIB
static
int
cp210x_gpio_request
(
struct
gpio_chip
*
gc
,
unsigned
int
offset
)
{
{
struct
cp210x_port_private
*
port_priv
;
struct
usb_serial
*
serial
=
gpiochip_get_data
(
gc
);
int
i
;
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
unsigned
int
partNum
;
if
(
priv
->
gpio_altfunc
&
BIT
(
offset
))
return
-
ENODEV
;
return
0
;
}
static
int
cp210x_gpio_get
(
struct
gpio_chip
*
gc
,
unsigned
int
gpio
)
{
struct
usb_serial
*
serial
=
gpiochip_get_data
(
gc
);
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
u8
req_type
=
REQTYPE_DEVICE_TO_HOST
;
int
result
;
u8
buf
;
if
(
priv
->
partnum
==
CP210X_PARTNUM_CP2105
)
req_type
=
REQTYPE_INTERFACE_TO_HOST
;
result
=
usb_autopm_get_interface
(
serial
->
interface
);
if
(
result
)
return
result
;
result
=
cp210x_read_vendor_block
(
serial
,
req_type
,
CP210X_READ_LATCH
,
&
buf
,
sizeof
(
buf
));
usb_autopm_put_interface
(
serial
->
interface
);
if
(
result
<
0
)
return
result
;
return
!!
(
buf
&
BIT
(
gpio
));
}
static
void
cp210x_gpio_set
(
struct
gpio_chip
*
gc
,
unsigned
int
gpio
,
int
value
)
{
struct
usb_serial
*
serial
=
gpiochip_get_data
(
gc
);
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
struct
cp210x_gpio_write
buf
;
int
result
;
/* cp210x buffers behave strangely unless device is reset */
if
(
value
==
1
)
usb_reset_device
(
serial
->
dev
);
buf
.
state
=
BIT
(
gpio
);
else
buf
.
state
=
0
;
buf
.
mask
=
BIT
(
gpio
);
result
=
usb_autopm_get_interface
(
serial
->
interface
);
if
(
result
)
goto
out
;
if
(
priv
->
partnum
==
CP210X_PARTNUM_CP2105
)
{
result
=
cp210x_write_vendor_block
(
serial
,
REQTYPE_HOST_TO_INTERFACE
,
CP210X_WRITE_LATCH
,
&
buf
,
sizeof
(
buf
));
}
else
{
u16
wIndex
=
buf
.
state
<<
8
|
buf
.
mask
;
result
=
usb_control_msg
(
serial
->
dev
,
usb_sndctrlpipe
(
serial
->
dev
,
0
),
CP210X_VENDOR_SPECIFIC
,
REQTYPE_HOST_TO_DEVICE
,
CP210X_WRITE_LATCH
,
wIndex
,
NULL
,
0
,
USB_CTRL_SET_TIMEOUT
);
}
for
(
i
=
0
;
i
<
serial
->
num_ports
;
i
++
)
{
usb_autopm_put_interface
(
serial
->
interface
);
port_priv
=
kzalloc
(
sizeof
(
*
port_priv
),
GFP_KERNEL
);
out:
if
(
!
port_priv
)
if
(
result
<
0
)
{
return
-
ENOMEM
;
dev_err
(
&
serial
->
interface
->
dev
,
"failed to set GPIO value: %d
\n
"
,
result
);
}
}
static
int
cp210x_gpio_direction_get
(
struct
gpio_chip
*
gc
,
unsigned
int
gpio
)
{
struct
usb_serial
*
serial
=
gpiochip_get_data
(
gc
);
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
memset
(
port_priv
,
0x00
,
sizeof
(
*
port_priv
));
return
priv
->
gpio_input
&
BIT
(
gpio
);
port_priv
->
bInterfaceNumber
=
}
serial
->
interface
->
cur_altsetting
->
desc
.
bInterfaceNumber
;
usb_set_serial_port_data
(
serial
->
port
[
i
],
port_priv
);
static
int
cp210x_gpio_direction_input
(
struct
gpio_chip
*
gc
,
unsigned
int
gpio
)
{
struct
usb_serial
*
serial
=
gpiochip_get_data
(
gc
);
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
/* Get the 1-byte part number of the cp210x device */
if
(
priv
->
partnum
==
CP210X_PARTNUM_CP2105
)
{
cp210x_get_config
(
serial
->
port
[
i
],
/* hardware does not support an input mode */
REQTYPE_DEVICE_TO_HOST
,
CP210X_VENDOR_SPECIFIC
,
return
-
ENOTSUPP
;
CP210X_GET_PARTNUM
,
&
partNum
,
1
);
port_priv
->
bPartNumber
=
partNum
&
0xFF
;
}
}
/* push-pull pins cannot be changed to be inputs */
if
(
priv
->
gpio_pushpull
&
BIT
(
gpio
))
return
-
EINVAL
;
/* make sure to release pin if it is being driven low */
cp210x_gpio_set
(
gc
,
gpio
,
1
);
priv
->
gpio_input
|=
BIT
(
gpio
);
return
0
;
return
0
;
}
}
static
void
cp210x_release
(
struct
usb_serial
*
serial
)
static
int
cp210x_gpio_direction_output
(
struct
gpio_chip
*
gc
,
unsigned
int
gpio
,
int
value
)
{
struct
usb_serial
*
serial
=
gpiochip_get_data
(
gc
);
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
priv
->
gpio_input
&=
~
BIT
(
gpio
);
cp210x_gpio_set
(
gc
,
gpio
,
value
);
return
0
;
}
static
int
cp210x_gpio_set_config
(
struct
gpio_chip
*
gc
,
unsigned
int
gpio
,
unsigned
long
config
)
{
struct
usb_serial
*
serial
=
gpiochip_get_data
(
gc
);
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
enum
pin_config_param
param
=
pinconf_to_config_param
(
config
);
/* Succeed only if in correct mode (this can't be set at runtime) */
if
((
param
==
PIN_CONFIG_DRIVE_PUSH_PULL
)
&&
(
priv
->
gpio_pushpull
&
BIT
(
gpio
)))
return
0
;
if
((
param
==
PIN_CONFIG_DRIVE_OPEN_DRAIN
)
&&
!
(
priv
->
gpio_pushpull
&
BIT
(
gpio
)))
return
0
;
return
-
ENOTSUPP
;
}
/*
* This function is for configuring GPIO using shared pins, where other signals
* are made unavailable by configuring the use of GPIO. This is believed to be
* only applicable to the cp2105 at this point, the other devices supported by
* this driver that provide GPIO do so in a way that does not impact other
* signals and are thus expected to have very different initialisation.
*/
static
int
cp2105_gpioconf_init
(
struct
usb_serial
*
serial
)
{
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
struct
cp210x_pin_mode
mode
;
struct
cp210x_dual_port_config
config
;
u8
intf_num
=
cp210x_interface_num
(
serial
);
u8
iface_config
;
int
result
;
result
=
cp210x_read_vendor_block
(
serial
,
REQTYPE_DEVICE_TO_HOST
,
CP210X_GET_DEVICEMODE
,
&
mode
,
sizeof
(
mode
));
if
(
result
<
0
)
return
result
;
result
=
cp210x_read_vendor_block
(
serial
,
REQTYPE_DEVICE_TO_HOST
,
CP210X_GET_PORTCONFIG
,
&
config
,
sizeof
(
config
));
if
(
result
<
0
)
return
result
;
/* 2 banks of GPIO - One for the pins taken from each serial port */
if
(
intf_num
==
0
)
{
if
(
mode
.
eci
==
CP210X_PIN_MODE_MODEM
)
{
/* mark all GPIOs of this interface as reserved */
priv
->
gpio_altfunc
=
0xff
;
return
0
;
}
iface_config
=
config
.
eci_cfg
;
priv
->
gpio_pushpull
=
(
u8
)((
le16_to_cpu
(
config
.
gpio_mode
)
&
CP210X_ECI_GPIO_MODE_MASK
)
>>
CP210X_ECI_GPIO_MODE_OFFSET
);
priv
->
gc
.
ngpio
=
2
;
}
else
if
(
intf_num
==
1
)
{
if
(
mode
.
sci
==
CP210X_PIN_MODE_MODEM
)
{
/* mark all GPIOs of this interface as reserved */
priv
->
gpio_altfunc
=
0xff
;
return
0
;
}
iface_config
=
config
.
sci_cfg
;
priv
->
gpio_pushpull
=
(
u8
)((
le16_to_cpu
(
config
.
gpio_mode
)
&
CP210X_SCI_GPIO_MODE_MASK
)
>>
CP210X_SCI_GPIO_MODE_OFFSET
);
priv
->
gc
.
ngpio
=
3
;
}
else
{
return
-
ENODEV
;
}
/* mark all pins which are not in GPIO mode */
if
(
iface_config
&
CP2105_GPIO0_TXLED_MODE
)
/* GPIO 0 */
priv
->
gpio_altfunc
|=
BIT
(
0
);
if
(
iface_config
&
(
CP2105_GPIO1_RXLED_MODE
|
/* GPIO 1 */
CP2105_GPIO1_RS485_MODE
))
priv
->
gpio_altfunc
|=
BIT
(
1
);
/* driver implementation for CP2105 only supports outputs */
priv
->
gpio_input
=
0
;
return
0
;
}
static
int
cp2104_gpioconf_init
(
struct
usb_serial
*
serial
)
{
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
struct
cp210x_single_port_config
config
;
u8
iface_config
;
u8
gpio_latch
;
int
result
;
u8
i
;
result
=
cp210x_read_vendor_block
(
serial
,
REQTYPE_DEVICE_TO_HOST
,
CP210X_GET_PORTCONFIG
,
&
config
,
sizeof
(
config
));
if
(
result
<
0
)
return
result
;
priv
->
gc
.
ngpio
=
4
;
iface_config
=
config
.
device_cfg
;
priv
->
gpio_pushpull
=
(
u8
)((
le16_to_cpu
(
config
.
gpio_mode
)
&
CP210X_GPIO_MODE_MASK
)
>>
CP210X_GPIO_MODE_OFFSET
);
gpio_latch
=
(
u8
)((
le16_to_cpu
(
config
.
reset_state
)
&
CP210X_GPIO_MODE_MASK
)
>>
CP210X_GPIO_MODE_OFFSET
);
/* mark all pins which are not in GPIO mode */
if
(
iface_config
&
CP2104_GPIO0_TXLED_MODE
)
/* GPIO 0 */
priv
->
gpio_altfunc
|=
BIT
(
0
);
if
(
iface_config
&
CP2104_GPIO1_RXLED_MODE
)
/* GPIO 1 */
priv
->
gpio_altfunc
|=
BIT
(
1
);
if
(
iface_config
&
CP2104_GPIO2_RS485_MODE
)
/* GPIO 2 */
priv
->
gpio_altfunc
|=
BIT
(
2
);
/*
* Like CP2102N, CP2104 has also no strict input and output pin
* modes.
* Do the same input mode emulation as CP2102N.
*/
for
(
i
=
0
;
i
<
priv
->
gc
.
ngpio
;
++
i
)
{
/*
* Set direction to "input" iff pin is open-drain and reset
* value is 1.
*/
if
(
!
(
priv
->
gpio_pushpull
&
BIT
(
i
))
&&
(
gpio_latch
&
BIT
(
i
)))
priv
->
gpio_input
|=
BIT
(
i
);
}
return
0
;
}
static
int
cp2102n_gpioconf_init
(
struct
usb_serial
*
serial
)
{
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
const
u16
config_size
=
0x02a6
;
u8
gpio_rst_latch
;
u8
config_version
;
u8
gpio_pushpull
;
u8
*
config_buf
;
u8
gpio_latch
;
u8
gpio_ctrl
;
int
result
;
u8
i
;
/*
* Retrieve device configuration from the device.
* The array received contains all customization settings done at the
* factory/manufacturer. Format of the array is documented at the
* time of writing at:
* https://www.silabs.com/community/interface/knowledge-base.entry.html/2017/03/31/cp2102n_setconfig-xsfa
*/
config_buf
=
kmalloc
(
config_size
,
GFP_KERNEL
);
if
(
!
config_buf
)
return
-
ENOMEM
;
result
=
cp210x_read_vendor_block
(
serial
,
REQTYPE_DEVICE_TO_HOST
,
CP210X_READ_2NCONFIG
,
config_buf
,
config_size
);
if
(
result
<
0
)
{
kfree
(
config_buf
);
return
result
;
}
config_version
=
config_buf
[
CP210X_2NCONFIG_CONFIG_VERSION_IDX
];
gpio_pushpull
=
config_buf
[
CP210X_2NCONFIG_GPIO_MODE_IDX
];
gpio_ctrl
=
config_buf
[
CP210X_2NCONFIG_GPIO_CONTROL_IDX
];
gpio_rst_latch
=
config_buf
[
CP210X_2NCONFIG_GPIO_RSTLATCH_IDX
];
kfree
(
config_buf
);
/* Make sure this is a config format we understand. */
if
(
config_version
!=
0x01
)
return
-
ENOTSUPP
;
priv
->
gc
.
ngpio
=
4
;
/*
* Get default pin states after reset. Needed so we can determine
* the direction of an open-drain pin.
*/
gpio_latch
=
(
gpio_rst_latch
>>
3
)
&
0x0f
;
/* 0 indicates open-drain mode, 1 is push-pull */
priv
->
gpio_pushpull
=
(
gpio_pushpull
>>
3
)
&
0x0f
;
/* 0 indicates GPIO mode, 1 is alternate function */
priv
->
gpio_altfunc
=
(
gpio_ctrl
>>
2
)
&
0x0f
;
if
(
priv
->
partnum
==
CP210X_PARTNUM_CP2102N_QFN28
)
{
/*
* For the QFN28 package, GPIO4-6 are controlled by
* the low three bits of the mode/latch fields.
* Contrary to the document linked above, the bits for
* the SUSPEND pins are elsewhere. No alternate
* function is available for these pins.
*/
priv
->
gc
.
ngpio
=
7
;
gpio_latch
|=
(
gpio_rst_latch
&
7
)
<<
4
;
priv
->
gpio_pushpull
|=
(
gpio_pushpull
&
7
)
<<
4
;
}
/*
* The CP2102N does not strictly has input and output pin modes,
* it only knows open-drain and push-pull modes which is set at
* factory. An open-drain pin can function both as an
* input or an output. We emulate input mode for open-drain pins
* by making sure they are not driven low, and we do not allow
* push-pull pins to be set as an input.
*/
for
(
i
=
0
;
i
<
priv
->
gc
.
ngpio
;
++
i
)
{
/*
* Set direction to "input" iff pin is open-drain and reset
* value is 1.
*/
if
(
!
(
priv
->
gpio_pushpull
&
BIT
(
i
))
&&
(
gpio_latch
&
BIT
(
i
)))
priv
->
gpio_input
|=
BIT
(
i
);
}
return
0
;
}
static
int
cp210x_gpio_init
(
struct
usb_serial
*
serial
)
{
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
int
result
;
switch
(
priv
->
partnum
)
{
case
CP210X_PARTNUM_CP2104
:
result
=
cp2104_gpioconf_init
(
serial
);
break
;
case
CP210X_PARTNUM_CP2105
:
result
=
cp2105_gpioconf_init
(
serial
);
break
;
case
CP210X_PARTNUM_CP2102N_QFN28
:
case
CP210X_PARTNUM_CP2102N_QFN24
:
case
CP210X_PARTNUM_CP2102N_QFN20
:
result
=
cp2102n_gpioconf_init
(
serial
);
break
;
default:
return
0
;
}
if
(
result
<
0
)
return
result
;
priv
->
gc
.
label
=
"cp210x"
;
priv
->
gc
.
request
=
cp210x_gpio_request
;
priv
->
gc
.
get_direction
=
cp210x_gpio_direction_get
;
priv
->
gc
.
direction_input
=
cp210x_gpio_direction_input
;
priv
->
gc
.
direction_output
=
cp210x_gpio_direction_output
;
priv
->
gc
.
get
=
cp210x_gpio_get
;
priv
->
gc
.
set
=
cp210x_gpio_set
;
priv
->
gc
.
set_config
=
cp210x_gpio_set_config
;
priv
->
gc
.
owner
=
THIS_MODULE
;
priv
->
gc
.
parent
=
&
serial
->
interface
->
dev
;
priv
->
gc
.
base
=
-
1
;
priv
->
gc
.
can_sleep
=
true
;
result
=
gpiochip_add_data
(
&
priv
->
gc
,
serial
);
if
(
!
result
)
priv
->
gpio_registered
=
true
;
return
result
;
}
static
void
cp210x_gpio_remove
(
struct
usb_serial
*
serial
)
{
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
if
(
priv
->
gpio_registered
)
{
gpiochip_remove
(
&
priv
->
gc
);
priv
->
gpio_registered
=
false
;
}
}
#else
static
int
cp210x_gpio_init
(
struct
usb_serial
*
serial
)
{
return
0
;
}
static
void
cp210x_gpio_remove
(
struct
usb_serial
*
serial
)
{
{
/* Nothing to do */
}
#endif
static
int
cp210x_port_probe
(
struct
usb_serial_port
*
port
)
{
struct
usb_serial
*
serial
=
port
->
serial
;
struct
cp210x_port_private
*
port_priv
;
struct
cp210x_port_private
*
port_priv
;
int
i
;
int
ret
;
port_priv
=
kzalloc
(
sizeof
(
*
port_priv
),
GFP_KERNEL
);
if
(
!
port_priv
)
return
-
ENOMEM
;
port_priv
->
bInterfaceNumber
=
cp210x_interface_num
(
serial
);
usb_set_serial_port_data
(
port
,
port_priv
);
for
(
i
=
0
;
i
<
serial
->
num_ports
;
i
++
)
{
ret
=
cp210x_detect_swapped_line_ctl
(
port
);
port_priv
=
usb_get_serial_port_data
(
serial
->
port
[
i
]);
if
(
ret
)
{
kfree
(
port_priv
);
kfree
(
port_priv
);
usb_set_serial_port_data
(
serial
->
port
[
i
],
NULL
)
;
return
ret
;
}
}
return
0
;
}
}
/*
static int
__init cp210x_init(void
)
static
int
cp210x_port_remove
(
struct
usb_serial_port
*
port
)
{
{
int retval;
struct
cp210x_port_private
*
port_priv
;
port_priv
=
usb_get_serial_port_data
(
port
);
kfree
(
port_priv
);
retval = usb_serial_register(&cp210x_device);
return
0
;
if (retval)
}
return retval;
retval = usb_register(&cp210x_driver);
static
void
cp210x_init_max_speed
(
struct
usb_serial
*
serial
)
if (retval) {
{
usb_serial_deregister(&cp210x_device);
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
return retval;
bool
use_actual_rate
=
false
;
speed_t
min
=
300
;
speed_t
max
;
switch
(
priv
->
partnum
)
{
case
CP210X_PARTNUM_CP2101
:
max
=
921600
;
break
;
case
CP210X_PARTNUM_CP2102
:
case
CP210X_PARTNUM_CP2103
:
max
=
1000000
;
break
;
case
CP210X_PARTNUM_CP2104
:
use_actual_rate
=
true
;
max
=
2000000
;
break
;
case
CP210X_PARTNUM_CP2108
:
max
=
2000000
;
break
;
case
CP210X_PARTNUM_CP2105
:
if
(
cp210x_interface_num
(
serial
)
==
0
)
{
use_actual_rate
=
true
;
max
=
2000000
;
/* ECI */
}
else
{
min
=
2400
;
max
=
921600
;
/* SCI */
}
break
;
case
CP210X_PARTNUM_CP2102N_QFN28
:
case
CP210X_PARTNUM_CP2102N_QFN24
:
case
CP210X_PARTNUM_CP2102N_QFN20
:
use_actual_rate
=
true
;
max
=
3000000
;
break
;
default:
max
=
2000000
;
break
;
}
priv
->
min_speed
=
min
;
priv
->
max_speed
=
max
;
priv
->
use_actual_rate
=
use_actual_rate
;
}
static
int
cp210x_attach
(
struct
usb_serial
*
serial
)
{
int
result
;
struct
cp210x_serial_private
*
priv
;
priv
=
kzalloc
(
sizeof
(
*
priv
),
GFP_KERNEL
);
if
(
!
priv
)
return
-
ENOMEM
;
result
=
cp210x_read_vendor_block
(
serial
,
REQTYPE_DEVICE_TO_HOST
,
CP210X_GET_PARTNUM
,
&
priv
->
partnum
,
sizeof
(
priv
->
partnum
));
if
(
result
<
0
)
{
dev_warn
(
&
serial
->
interface
->
dev
,
"querying part number failed
\n
"
);
priv
->
partnum
=
CP210X_PARTNUM_UNKNOWN
;
}
usb_set_serial_data
(
serial
,
priv
);
cp210x_init_max_speed
(
serial
);
result
=
cp210x_gpio_init
(
serial
);
if
(
result
<
0
)
{
dev_err
(
&
serial
->
interface
->
dev
,
"GPIO initialisation failed: %d
\n
"
,
result
);
}
}
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
return
0
;
return
0
;
}
}
static void
__exit cp210x_exit(void
)
static
void
cp210x_disconnect
(
struct
usb_serial
*
serial
)
{
{
usb_deregister(&cp210x_driver);
cp210x_gpio_remove
(
serial
);
usb_serial_deregister(&cp210x_device);
}
}
module_init(cp210x_init);
static
void
cp210x_release
(
struct
usb_serial
*
serial
)
module_exit(cp210x_exit);
{
*/
struct
cp210x_serial_private
*
priv
=
usb_get_serial_data
(
serial
);
cp210x_gpio_remove
(
serial
);
kfree
(
priv
);
}
module_usb_serial_driver
(
serial_drivers
,
id_table
);
module_usb_serial_driver
(
serial_drivers
,
id_table
);
MODULE_DESCRIPTION
(
DRIVER_DESC
);
MODULE_DESCRIPTION
(
DRIVER_DESC
);
MODULE_VERSION
(
DRIVER_VERSION
);
MODULE_LICENSE
(
"GPL v2"
);
MODULE_LICENSE
(
"GPL"
);
module_param
(
debug
,
bool
,
S_IRUGO
|
S_IWUSR
);
MODULE_PARM_DESC
(
debug
,
"Enable verbose debugging messages"
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment