forked from Minki/linux
Merge branch 'serial-from-alan'
* serial-from-alan: (79 commits) moxa: prevent opening unavailable ports imx: serial: use tty_encode_baud_rate to set true rate imx: serial: add IrDA support to serial driver imx: serial: use rational library function lib: isolate rational fractions helper function imx: serial: handle initialisation failure correctly imx: serial: be sure to stop xmit upon shutdown imx: serial: notify higher layers in case xmit IRQ was not called imx: serial: fix one bit field type imx: serial: fix whitespaces (no changes in functionality) tty: use prepare/finish_wait tty: remove sleep_on sierra: driver interface blacklisting sierra: driver urb handling improvements tty: resolve some sierra breakage timbuart: Fix the termios logic serial: Added Timberdale UART driver tty: Add URL for ttydev queue devpts: unregister the file system on error tty: Untangle termios and mm mutex dependencies ...
This commit is contained in:
commit
49c355617f
@ -71,7 +71,7 @@ P: Person
|
||||
M: Mail patches to
|
||||
L: Mailing list that is relevant to this area
|
||||
W: Web-page with status/info
|
||||
T: SCM tree type and location. Type is one of: git, hg, quilt.
|
||||
T: SCM tree type and location. Type is one of: git, hg, quilt, stgit.
|
||||
S: Status, one of the following:
|
||||
|
||||
Supported: Someone is actually paid to look after this.
|
||||
@ -159,7 +159,8 @@ F: drivers/net/r8169.c
|
||||
8250/16?50 (AND CLONE UARTS) SERIAL DRIVER
|
||||
L: linux-serial@vger.kernel.org
|
||||
W: http://serial.sourceforge.net
|
||||
S: Orphan
|
||||
M: alan@lxorguk.ukuu.org.uk
|
||||
S: Odd Fixes
|
||||
F: drivers/serial/8250*
|
||||
F: include/linux/serial_8250.h
|
||||
|
||||
@ -5629,6 +5630,7 @@ P: Alan Cox
|
||||
M: alan@lxorguk.ukuu.org.uk
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
T: stgit http://zeniv.linux.org.uk/~alan/ttydev/
|
||||
|
||||
TULIP NETWORK DRIVERS
|
||||
P: Grant Grundler
|
||||
|
@ -20,11 +20,16 @@
|
||||
#define ASMARM_ARCH_UART_H
|
||||
|
||||
#define IMXUART_HAVE_RTSCTS (1<<0)
|
||||
#define IMXUART_IRDA (1<<1)
|
||||
|
||||
struct imxuart_platform_data {
|
||||
int (*init)(struct platform_device *pdev);
|
||||
int (*exit)(struct platform_device *pdev);
|
||||
unsigned int flags;
|
||||
void (*irda_enable)(int enable);
|
||||
unsigned int irda_inv_rx:1;
|
||||
unsigned int irda_inv_tx:1;
|
||||
unsigned short transceiver_delay;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -67,6 +67,7 @@ static inline int user_termio_to_kernel_termios(struct ktermios *termios,
|
||||
SET_LOW_TERMIOS_BITS(termios, termio, c_oflag);
|
||||
SET_LOW_TERMIOS_BITS(termios, termio, c_cflag);
|
||||
SET_LOW_TERMIOS_BITS(termios, termio, c_lflag);
|
||||
get_user(termios->c_line, &termio->c_line);
|
||||
return copy_from_user(termios->c_cc, termio->c_cc, NCC);
|
||||
}
|
||||
|
||||
|
@ -277,8 +277,8 @@ static int hci_uart_tty_open(struct tty_struct *tty)
|
||||
/* FIXME: why is this needed. Note don't use ldisc_ref here as the
|
||||
open path is before the ldisc is referencable */
|
||||
|
||||
if (tty->ldisc.ops->flush_buffer)
|
||||
tty->ldisc.ops->flush_buffer(tty);
|
||||
if (tty->ldisc->ops->flush_buffer)
|
||||
tty->ldisc->ops->flush_buffer(tty);
|
||||
tty_driver_flush_buffer(tty);
|
||||
|
||||
return 0;
|
||||
@ -463,7 +463,6 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
|
||||
clear_bit(HCI_UART_PROTO_SET, &hu->flags);
|
||||
return err;
|
||||
}
|
||||
tty->low_latency = 1;
|
||||
} else
|
||||
return -EBUSY;
|
||||
break;
|
||||
|
@ -97,6 +97,19 @@ config DEVKMEM
|
||||
kind of kernel debugging operations.
|
||||
When in doubt, say "N".
|
||||
|
||||
config BFIN_JTAG_COMM
|
||||
tristate "Blackfin JTAG Communication"
|
||||
depends on BLACKFIN
|
||||
help
|
||||
Add support for emulating a TTY device over the Blackfin JTAG.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bfin_jtag_comm.
|
||||
|
||||
config BFIN_JTAG_COMM_CONSOLE
|
||||
bool "Console on Blackfin JTAG"
|
||||
depends on BFIN_JTAG_COMM=y
|
||||
|
||||
config SERIAL_NONSTANDARD
|
||||
bool "Non-standard serial port support"
|
||||
depends on HAS_IOMEM
|
||||
|
@ -13,6 +13,7 @@ obj-$(CONFIG_LEGACY_PTYS) += pty.o
|
||||
obj-$(CONFIG_UNIX98_PTYS) += pty.o
|
||||
obj-y += misc.o
|
||||
obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o
|
||||
obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o
|
||||
obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o
|
||||
obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o
|
||||
obj-$(CONFIG_AUDIT) += tty_audit.o
|
||||
|
365
drivers/char/bfin_jtag_comm.c
Normal file
365
drivers/char/bfin_jtag_comm.c
Normal file
@ -0,0 +1,365 @@
|
||||
/*
|
||||
* TTY over Blackfin JTAG Communication
|
||||
*
|
||||
* Copyright 2008-2009 Analog Devices Inc.
|
||||
*
|
||||
* Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/circ_buf.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
/* See the Debug/Emulation chapter in the HRM */
|
||||
#define EMUDOF 0x00000001 /* EMUDAT_OUT full & valid */
|
||||
#define EMUDIF 0x00000002 /* EMUDAT_IN full & valid */
|
||||
#define EMUDOOVF 0x00000004 /* EMUDAT_OUT overflow */
|
||||
#define EMUDIOVF 0x00000008 /* EMUDAT_IN overflow */
|
||||
|
||||
#define DRV_NAME "bfin-jtag-comm"
|
||||
#define DEV_NAME "ttyBFJC"
|
||||
|
||||
#define pr_init(fmt, args...) ({ static const __initdata char __fmt[] = fmt; printk(__fmt, ## args); })
|
||||
#define debug(fmt, args...) pr_debug(DRV_NAME ": " fmt, ## args)
|
||||
|
||||
static inline uint32_t bfin_write_emudat(uint32_t emudat)
|
||||
{
|
||||
__asm__ __volatile__("emudat = %0;" : : "d"(emudat));
|
||||
return emudat;
|
||||
}
|
||||
|
||||
static inline uint32_t bfin_read_emudat(void)
|
||||
{
|
||||
uint32_t emudat;
|
||||
__asm__ __volatile__("%0 = emudat;" : "=d"(emudat));
|
||||
return emudat;
|
||||
}
|
||||
|
||||
static inline uint32_t bfin_write_emudat_chars(char a, char b, char c, char d)
|
||||
{
|
||||
return bfin_write_emudat((a << 0) | (b << 8) | (c << 16) | (d << 24));
|
||||
}
|
||||
|
||||
#define CIRC_SIZE 2048 /* see comment in tty_io.c:do_tty_write() */
|
||||
#define CIRC_MASK (CIRC_SIZE - 1)
|
||||
#define circ_empty(circ) ((circ)->head == (circ)->tail)
|
||||
#define circ_free(circ) CIRC_SPACE((circ)->head, (circ)->tail, CIRC_SIZE)
|
||||
#define circ_cnt(circ) CIRC_CNT((circ)->head, (circ)->tail, CIRC_SIZE)
|
||||
#define circ_byte(circ, idx) ((circ)->buf[(idx) & CIRC_MASK])
|
||||
|
||||
static struct tty_driver *bfin_jc_driver;
|
||||
static struct task_struct *bfin_jc_kthread;
|
||||
static struct tty_struct * volatile bfin_jc_tty;
|
||||
static unsigned long bfin_jc_count;
|
||||
static DEFINE_MUTEX(bfin_jc_tty_mutex);
|
||||
static volatile struct circ_buf bfin_jc_write_buf;
|
||||
|
||||
static int
|
||||
bfin_jc_emudat_manager(void *arg)
|
||||
{
|
||||
uint32_t inbound_len = 0, outbound_len = 0;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
/* no one left to give data to, so sleep */
|
||||
if (bfin_jc_tty == NULL && circ_empty(&bfin_jc_write_buf)) {
|
||||
debug("waiting for readers\n");
|
||||
__set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule();
|
||||
__set_current_state(TASK_RUNNING);
|
||||
}
|
||||
|
||||
/* no data available, so just chill */
|
||||
if (!(bfin_read_DBGSTAT() & EMUDIF) && circ_empty(&bfin_jc_write_buf)) {
|
||||
debug("waiting for data (in_len = %i) (circ: %i %i)\n",
|
||||
inbound_len, bfin_jc_write_buf.tail, bfin_jc_write_buf.head);
|
||||
if (inbound_len)
|
||||
schedule();
|
||||
else
|
||||
schedule_timeout_interruptible(HZ);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if incoming data is ready, eat it */
|
||||
if (bfin_read_DBGSTAT() & EMUDIF) {
|
||||
struct tty_struct *tty;
|
||||
mutex_lock(&bfin_jc_tty_mutex);
|
||||
tty = (struct tty_struct *)bfin_jc_tty;
|
||||
if (tty != NULL) {
|
||||
uint32_t emudat = bfin_read_emudat();
|
||||
if (inbound_len == 0) {
|
||||
debug("incoming length: 0x%08x\n", emudat);
|
||||
inbound_len = emudat;
|
||||
} else {
|
||||
size_t num_chars = (4 <= inbound_len ? 4 : inbound_len);
|
||||
debug(" incoming data: 0x%08x (pushing %zu)\n", emudat, num_chars);
|
||||
inbound_len -= num_chars;
|
||||
tty_insert_flip_string(tty, (unsigned char *)&emudat, num_chars);
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&bfin_jc_tty_mutex);
|
||||
}
|
||||
|
||||
/* if outgoing data is ready, post it */
|
||||
if (!(bfin_read_DBGSTAT() & EMUDOF) && !circ_empty(&bfin_jc_write_buf)) {
|
||||
if (outbound_len == 0) {
|
||||
outbound_len = circ_cnt(&bfin_jc_write_buf);
|
||||
bfin_write_emudat(outbound_len);
|
||||
debug("outgoing length: 0x%08x\n", outbound_len);
|
||||
} else {
|
||||
struct tty_struct *tty;
|
||||
int tail = bfin_jc_write_buf.tail;
|
||||
size_t ate = (4 <= outbound_len ? 4 : outbound_len);
|
||||
uint32_t emudat =
|
||||
bfin_write_emudat_chars(
|
||||
circ_byte(&bfin_jc_write_buf, tail + 0),
|
||||
circ_byte(&bfin_jc_write_buf, tail + 1),
|
||||
circ_byte(&bfin_jc_write_buf, tail + 2),
|
||||
circ_byte(&bfin_jc_write_buf, tail + 3)
|
||||
);
|
||||
bfin_jc_write_buf.tail += ate;
|
||||
outbound_len -= ate;
|
||||
mutex_lock(&bfin_jc_tty_mutex);
|
||||
tty = (struct tty_struct *)bfin_jc_tty;
|
||||
if (tty)
|
||||
tty_wakeup(tty);
|
||||
mutex_unlock(&bfin_jc_tty_mutex);
|
||||
debug(" outgoing data: 0x%08x (pushing %zu)\n", emudat, ate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__set_current_state(TASK_RUNNING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bfin_jc_open(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
mutex_lock(&bfin_jc_tty_mutex);
|
||||
debug("open %lu\n", bfin_jc_count);
|
||||
++bfin_jc_count;
|
||||
bfin_jc_tty = tty;
|
||||
wake_up_process(bfin_jc_kthread);
|
||||
mutex_unlock(&bfin_jc_tty_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
bfin_jc_close(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
mutex_lock(&bfin_jc_tty_mutex);
|
||||
debug("close %lu\n", bfin_jc_count);
|
||||
if (--bfin_jc_count == 0)
|
||||
bfin_jc_tty = NULL;
|
||||
wake_up_process(bfin_jc_kthread);
|
||||
mutex_unlock(&bfin_jc_tty_mutex);
|
||||
}
|
||||
|
||||
/* XXX: we dont handle the put_char() case where we must handle count = 1 */
|
||||
static int
|
||||
bfin_jc_circ_write(const unsigned char *buf, int count)
|
||||
{
|
||||
int i;
|
||||
count = min(count, circ_free(&bfin_jc_write_buf));
|
||||
debug("going to write chunk of %i bytes\n", count);
|
||||
for (i = 0; i < count; ++i)
|
||||
circ_byte(&bfin_jc_write_buf, bfin_jc_write_buf.head + i) = buf[i];
|
||||
bfin_jc_write_buf.head += i;
|
||||
return i;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_BFIN_JTAG_COMM_CONSOLE
|
||||
# define acquire_console_sem()
|
||||
# define release_console_sem()
|
||||
#endif
|
||||
static int
|
||||
bfin_jc_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
||||
{
|
||||
int i;
|
||||
acquire_console_sem();
|
||||
i = bfin_jc_circ_write(buf, count);
|
||||
release_console_sem();
|
||||
wake_up_process(bfin_jc_kthread);
|
||||
return i;
|
||||
}
|
||||
|
||||
static void
|
||||
bfin_jc_flush_chars(struct tty_struct *tty)
|
||||
{
|
||||
wake_up_process(bfin_jc_kthread);
|
||||
}
|
||||
|
||||
static int
|
||||
bfin_jc_write_room(struct tty_struct *tty)
|
||||
{
|
||||
return circ_free(&bfin_jc_write_buf);
|
||||
}
|
||||
|
||||
static int
|
||||
bfin_jc_chars_in_buffer(struct tty_struct *tty)
|
||||
{
|
||||
return circ_cnt(&bfin_jc_write_buf);
|
||||
}
|
||||
|
||||
static void
|
||||
bfin_jc_wait_until_sent(struct tty_struct *tty, int timeout)
|
||||
{
|
||||
unsigned long expire = jiffies + timeout;
|
||||
while (!circ_empty(&bfin_jc_write_buf)) {
|
||||
if (signal_pending(current))
|
||||
break;
|
||||
if (time_after(jiffies, expire))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct tty_operations bfin_jc_ops = {
|
||||
.open = bfin_jc_open,
|
||||
.close = bfin_jc_close,
|
||||
.write = bfin_jc_write,
|
||||
/*.put_char = bfin_jc_put_char,*/
|
||||
.flush_chars = bfin_jc_flush_chars,
|
||||
.write_room = bfin_jc_write_room,
|
||||
.chars_in_buffer = bfin_jc_chars_in_buffer,
|
||||
.wait_until_sent = bfin_jc_wait_until_sent,
|
||||
};
|
||||
|
||||
static int __init bfin_jc_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
bfin_jc_kthread = kthread_create(bfin_jc_emudat_manager, NULL, DRV_NAME);
|
||||
if (IS_ERR(bfin_jc_kthread))
|
||||
return PTR_ERR(bfin_jc_kthread);
|
||||
|
||||
ret = -ENOMEM;
|
||||
|
||||
bfin_jc_write_buf.head = bfin_jc_write_buf.tail = 0;
|
||||
bfin_jc_write_buf.buf = kmalloc(CIRC_SIZE, GFP_KERNEL);
|
||||
if (!bfin_jc_write_buf.buf)
|
||||
goto err;
|
||||
|
||||
bfin_jc_driver = alloc_tty_driver(1);
|
||||
if (!bfin_jc_driver)
|
||||
goto err;
|
||||
|
||||
bfin_jc_driver->owner = THIS_MODULE;
|
||||
bfin_jc_driver->driver_name = DRV_NAME;
|
||||
bfin_jc_driver->name = DEV_NAME;
|
||||
bfin_jc_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
bfin_jc_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||
bfin_jc_driver->init_termios = tty_std_termios;
|
||||
tty_set_operations(bfin_jc_driver, &bfin_jc_ops);
|
||||
|
||||
ret = tty_register_driver(bfin_jc_driver);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
pr_init(KERN_INFO DRV_NAME ": initialized\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
put_tty_driver(bfin_jc_driver);
|
||||
kfree(bfin_jc_write_buf.buf);
|
||||
kthread_stop(bfin_jc_kthread);
|
||||
return ret;
|
||||
}
|
||||
module_init(bfin_jc_init);
|
||||
|
||||
static void __exit bfin_jc_exit(void)
|
||||
{
|
||||
kthread_stop(bfin_jc_kthread);
|
||||
kfree(bfin_jc_write_buf.buf);
|
||||
tty_unregister_driver(bfin_jc_driver);
|
||||
put_tty_driver(bfin_jc_driver);
|
||||
}
|
||||
module_exit(bfin_jc_exit);
|
||||
|
||||
#if defined(CONFIG_BFIN_JTAG_COMM_CONSOLE) || defined(CONFIG_EARLY_PRINTK)
|
||||
static void
|
||||
bfin_jc_straight_buffer_write(const char *buf, unsigned count)
|
||||
{
|
||||
unsigned ate = 0;
|
||||
while (bfin_read_DBGSTAT() & EMUDOF)
|
||||
continue;
|
||||
bfin_write_emudat(count);
|
||||
while (ate < count) {
|
||||
while (bfin_read_DBGSTAT() & EMUDOF)
|
||||
continue;
|
||||
bfin_write_emudat_chars(buf[ate], buf[ate+1], buf[ate+2], buf[ate+3]);
|
||||
ate += 4;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BFIN_JTAG_COMM_CONSOLE
|
||||
static void
|
||||
bfin_jc_console_write(struct console *co, const char *buf, unsigned count)
|
||||
{
|
||||
if (bfin_jc_kthread == NULL)
|
||||
bfin_jc_straight_buffer_write(buf, count);
|
||||
else
|
||||
bfin_jc_circ_write(buf, count);
|
||||
}
|
||||
|
||||
static struct tty_driver *
|
||||
bfin_jc_console_device(struct console *co, int *index)
|
||||
{
|
||||
*index = co->index;
|
||||
return bfin_jc_driver;
|
||||
}
|
||||
|
||||
static struct console bfin_jc_console = {
|
||||
.name = DEV_NAME,
|
||||
.write = bfin_jc_console_write,
|
||||
.device = bfin_jc_console_device,
|
||||
.flags = CON_ANYTIME | CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
static int __init bfin_jc_console_init(void)
|
||||
{
|
||||
register_console(&bfin_jc_console);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(bfin_jc_console_init);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_EARLY_PRINTK
|
||||
static void __init
|
||||
bfin_jc_early_write(struct console *co, const char *buf, unsigned int count)
|
||||
{
|
||||
bfin_jc_straight_buffer_write(buf, count);
|
||||
}
|
||||
|
||||
static struct __initdata console bfin_jc_early_console = {
|
||||
.name = "early_BFJC",
|
||||
.write = bfin_jc_early_write,
|
||||
.flags = CON_ANYTIME | CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
struct console * __init
|
||||
bfin_jc_early_init(unsigned int port, unsigned int cflag)
|
||||
{
|
||||
return &bfin_jc_early_console;
|
||||
}
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
|
||||
MODULE_DESCRIPTION("TTY over Blackfin JTAG Communication");
|
||||
MODULE_LICENSE("GPL");
|
@ -604,7 +604,6 @@
|
||||
|
||||
#define NR_PORTS 256
|
||||
|
||||
#define ZE_V1_NPORTS 64
|
||||
#define ZO_V1 0
|
||||
#define ZO_V2 1
|
||||
#define ZE_V1 2
|
||||
@ -663,18 +662,6 @@
|
||||
static void cy_throttle(struct tty_struct *tty);
|
||||
static void cy_send_xchar(struct tty_struct *tty, char ch);
|
||||
|
||||
#define IS_CYC_Z(card) ((card).num_chips == (unsigned int)-1)
|
||||
|
||||
#define Z_FPGA_CHECK(card) \
|
||||
((readl(&((struct RUNTIME_9060 __iomem *) \
|
||||
((card).ctl_addr))->init_ctrl) & (1<<17)) != 0)
|
||||
|
||||
#define ISZLOADED(card) (((ZO_V1 == readl(&((struct RUNTIME_9060 __iomem *) \
|
||||
((card).ctl_addr))->mail_box_0)) || \
|
||||
Z_FPGA_CHECK(card)) && \
|
||||
(ZFIRM_ID == readl(&((struct FIRM_ID __iomem *) \
|
||||
((card).base_addr+ID_ADDRESS))->signature)))
|
||||
|
||||
#ifndef SERIAL_XMIT_SIZE
|
||||
#define SERIAL_XMIT_SIZE (min(PAGE_SIZE, 4096))
|
||||
#endif
|
||||
@ -687,8 +674,6 @@ static void cy_send_xchar(struct tty_struct *tty, char ch);
|
||||
#define DRIVER_VERSION 0x02010203
|
||||
#define RAM_SIZE 0x80000
|
||||
|
||||
#define Z_FPGA_LOADED(X) ((readl(&(X)->init_ctrl) & (1<<17)) != 0)
|
||||
|
||||
enum zblock_type {
|
||||
ZBLOCK_PRG = 0,
|
||||
ZBLOCK_FPGA = 1
|
||||
@ -883,6 +868,29 @@ static void cyz_rx_restart(unsigned long);
|
||||
static struct timer_list cyz_rx_full_timer[NR_PORTS];
|
||||
#endif /* CONFIG_CYZ_INTR */
|
||||
|
||||
static inline bool cy_is_Z(struct cyclades_card *card)
|
||||
{
|
||||
return card->num_chips == (unsigned int)-1;
|
||||
}
|
||||
|
||||
static inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr)
|
||||
{
|
||||
return readl(&ctl_addr->init_ctrl) & (1 << 17);
|
||||
}
|
||||
|
||||
static inline bool cyz_fpga_loaded(struct cyclades_card *card)
|
||||
{
|
||||
return __cyz_fpga_loaded(card->ctl_addr.p9060);
|
||||
}
|
||||
|
||||
static inline bool cyz_is_loaded(struct cyclades_card *card)
|
||||
{
|
||||
struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS;
|
||||
|
||||
return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) &&
|
||||
readl(&fw_id->signature) == ZFIRM_ID;
|
||||
}
|
||||
|
||||
static inline int serial_paranoia_check(struct cyclades_port *info,
|
||||
char *name, const char *routine)
|
||||
{
|
||||
@ -1395,19 +1403,15 @@ cyz_fetch_msg(struct cyclades_card *cinfo,
|
||||
unsigned long loc_doorbell;
|
||||
|
||||
firm_id = cinfo->base_addr + ID_ADDRESS;
|
||||
if (!ISZLOADED(*cinfo))
|
||||
return -1;
|
||||
zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff);
|
||||
board_ctrl = &zfw_ctrl->board_ctrl;
|
||||
|
||||
loc_doorbell = readl(&((struct RUNTIME_9060 __iomem *)
|
||||
(cinfo->ctl_addr))->loc_doorbell);
|
||||
loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell);
|
||||
if (loc_doorbell) {
|
||||
*cmd = (char)(0xff & loc_doorbell);
|
||||
*channel = readl(&board_ctrl->fwcmd_channel);
|
||||
*param = (__u32) readl(&board_ctrl->fwcmd_param);
|
||||
cy_writel(&((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))->
|
||||
loc_doorbell, 0xffffffff);
|
||||
cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -1424,15 +1428,14 @@ cyz_issue_cmd(struct cyclades_card *cinfo,
|
||||
unsigned int index;
|
||||
|
||||
firm_id = cinfo->base_addr + ID_ADDRESS;
|
||||
if (!ISZLOADED(*cinfo))
|
||||
if (!cyz_is_loaded(cinfo))
|
||||
return -1;
|
||||
|
||||
zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff);
|
||||
board_ctrl = &zfw_ctrl->board_ctrl;
|
||||
|
||||
index = 0;
|
||||
pci_doorbell =
|
||||
&((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))->pci_doorbell;
|
||||
pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell;
|
||||
while ((readl(pci_doorbell) & 0xff) != 0) {
|
||||
if (index++ == 1000)
|
||||
return (int)(readl(pci_doorbell) & 0xff);
|
||||
@ -1624,10 +1627,8 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo)
|
||||
static struct BOARD_CTRL __iomem *board_ctrl;
|
||||
static struct CH_CTRL __iomem *ch_ctrl;
|
||||
static struct BUF_CTRL __iomem *buf_ctrl;
|
||||
__u32 channel;
|
||||
__u32 channel, param, fw_ver;
|
||||
__u8 cmd;
|
||||
__u32 param;
|
||||
__u32 hw_ver, fw_ver;
|
||||
int special_count;
|
||||
int delta_count;
|
||||
|
||||
@ -1635,8 +1636,6 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo)
|
||||
zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff);
|
||||
board_ctrl = &zfw_ctrl->board_ctrl;
|
||||
fw_ver = readl(&board_ctrl->fw_version);
|
||||
hw_ver = readl(&((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))->
|
||||
mail_box_0);
|
||||
|
||||
while (cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) {
|
||||
special_count = 0;
|
||||
@ -1737,15 +1736,7 @@ static irqreturn_t cyz_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct cyclades_card *cinfo = dev_id;
|
||||
|
||||
if (unlikely(cinfo == NULL)) {
|
||||
#ifdef CY_DEBUG_INTERRUPTS
|
||||
printk(KERN_DEBUG "cyz_interrupt: spurious interrupt %d\n",
|
||||
irq);
|
||||
#endif
|
||||
return IRQ_NONE; /* spurious interrupt */
|
||||
}
|
||||
|
||||
if (unlikely(!ISZLOADED(*cinfo))) {
|
||||
if (unlikely(!cyz_is_loaded(cinfo))) {
|
||||
#ifdef CY_DEBUG_INTERRUPTS
|
||||
printk(KERN_DEBUG "cyz_interrupt: board not yet loaded "
|
||||
"(IRQ%d).\n", irq);
|
||||
@ -1785,7 +1776,6 @@ static void cyz_poll(unsigned long arg)
|
||||
struct tty_struct *tty;
|
||||
struct FIRM_ID __iomem *firm_id;
|
||||
struct ZFW_CTRL __iomem *zfw_ctrl;
|
||||
struct BOARD_CTRL __iomem *board_ctrl;
|
||||
struct BUF_CTRL __iomem *buf_ctrl;
|
||||
unsigned long expires = jiffies + HZ;
|
||||
unsigned int port, card;
|
||||
@ -1793,19 +1783,17 @@ static void cyz_poll(unsigned long arg)
|
||||
for (card = 0; card < NR_CARDS; card++) {
|
||||
cinfo = &cy_card[card];
|
||||
|
||||
if (!IS_CYC_Z(*cinfo))
|
||||
if (!cy_is_Z(cinfo))
|
||||
continue;
|
||||
if (!ISZLOADED(*cinfo))
|
||||
if (!cyz_is_loaded(cinfo))
|
||||
continue;
|
||||
|
||||
firm_id = cinfo->base_addr + ID_ADDRESS;
|
||||
zfw_ctrl = cinfo->base_addr +
|
||||
(readl(&firm_id->zfwctrl_addr) & 0xfffff);
|
||||
board_ctrl = &(zfw_ctrl->board_ctrl);
|
||||
|
||||
/* Skip first polling cycle to avoid racing conditions with the FW */
|
||||
if (!cinfo->intr_enabled) {
|
||||
cinfo->nports = (int)readl(&board_ctrl->n_channel);
|
||||
cinfo->intr_enabled = 1;
|
||||
continue;
|
||||
}
|
||||
@ -1874,7 +1862,7 @@ static int startup(struct cyclades_port *info)
|
||||
|
||||
set_line_char(info);
|
||||
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -1931,7 +1919,7 @@ static int startup(struct cyclades_port *info)
|
||||
base_addr = card->base_addr;
|
||||
|
||||
firm_id = base_addr + ID_ADDRESS;
|
||||
if (!ISZLOADED(*card))
|
||||
if (!cyz_is_loaded(card))
|
||||
return -ENODEV;
|
||||
|
||||
zfw_ctrl = card->base_addr +
|
||||
@ -2026,7 +2014,7 @@ static void start_xmit(struct cyclades_port *info)
|
||||
|
||||
card = info->card;
|
||||
channel = info->line - card->first_line;
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -2070,7 +2058,7 @@ static void shutdown(struct cyclades_port *info)
|
||||
|
||||
card = info->card;
|
||||
channel = info->line - card->first_line;
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -2126,7 +2114,7 @@ static void shutdown(struct cyclades_port *info)
|
||||
#endif
|
||||
|
||||
firm_id = base_addr + ID_ADDRESS;
|
||||
if (!ISZLOADED(*card))
|
||||
if (!cyz_is_loaded(card))
|
||||
return;
|
||||
|
||||
zfw_ctrl = card->base_addr +
|
||||
@ -2233,7 +2221,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp,
|
||||
#endif
|
||||
info->port.blocked_open++;
|
||||
|
||||
if (!IS_CYC_Z(*cinfo)) {
|
||||
if (!cy_is_Z(cinfo)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = cinfo->bus_index;
|
||||
@ -2296,7 +2284,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp,
|
||||
|
||||
base_addr = cinfo->base_addr;
|
||||
firm_id = base_addr + ID_ADDRESS;
|
||||
if (!ISZLOADED(*cinfo)) {
|
||||
if (!cyz_is_loaded(cinfo)) {
|
||||
__set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&info->port.open_wait, &wait);
|
||||
return -EINVAL;
|
||||
@ -2397,16 +2385,14 @@ static int cy_open(struct tty_struct *tty, struct file *filp)
|
||||
treat it as absent from the system. This
|
||||
will make the user pay attention.
|
||||
*/
|
||||
if (IS_CYC_Z(*info->card)) {
|
||||
if (cy_is_Z(info->card)) {
|
||||
struct cyclades_card *cinfo = info->card;
|
||||
struct FIRM_ID __iomem *firm_id = cinfo->base_addr + ID_ADDRESS;
|
||||
|
||||
if (!ISZLOADED(*cinfo)) {
|
||||
if (((ZE_V1 == readl(&((struct RUNTIME_9060 __iomem *)
|
||||
(cinfo->ctl_addr))->mail_box_0)) &&
|
||||
Z_FPGA_CHECK(*cinfo)) &&
|
||||
(ZFIRM_HLT == readl(
|
||||
&firm_id->signature))) {
|
||||
if (!cyz_is_loaded(cinfo)) {
|
||||
if (cinfo->hw_ver == ZE_V1 && cyz_fpga_loaded(cinfo) &&
|
||||
readl(&firm_id->signature) ==
|
||||
ZFIRM_HLT) {
|
||||
printk(KERN_ERR "cyc:Cyclades-Z Error: you "
|
||||
"need an external power supply for "
|
||||
"this number of ports.\nFirmware "
|
||||
@ -2423,18 +2409,13 @@ static int cy_open(struct tty_struct *tty, struct file *filp)
|
||||
interrupts should be enabled as soon as the first open
|
||||
happens to one of its ports. */
|
||||
if (!cinfo->intr_enabled) {
|
||||
struct ZFW_CTRL __iomem *zfw_ctrl;
|
||||
struct BOARD_CTRL __iomem *board_ctrl;
|
||||
|
||||
zfw_ctrl = cinfo->base_addr +
|
||||
(readl(&firm_id->zfwctrl_addr) &
|
||||
0xfffff);
|
||||
|
||||
board_ctrl = &zfw_ctrl->board_ctrl;
|
||||
u16 intr;
|
||||
|
||||
/* Enable interrupts on the PLX chip */
|
||||
cy_writew(cinfo->ctl_addr + 0x68,
|
||||
readw(cinfo->ctl_addr + 0x68) | 0x0900);
|
||||
intr = readw(&cinfo->ctl_addr.p9060->
|
||||
intr_ctrl_stat) | 0x0900;
|
||||
cy_writew(&cinfo->ctl_addr.p9060->
|
||||
intr_ctrl_stat, intr);
|
||||
/* Enable interrupts on the FW */
|
||||
retval = cyz_issue_cmd(cinfo, 0,
|
||||
C_CM_IRQ_ENBL, 0L);
|
||||
@ -2442,8 +2423,6 @@ static int cy_open(struct tty_struct *tty, struct file *filp)
|
||||
printk(KERN_ERR "cyc:IRQ enable retval "
|
||||
"was %x\n", retval);
|
||||
}
|
||||
cinfo->nports =
|
||||
(int)readl(&board_ctrl->n_channel);
|
||||
cinfo->intr_enabled = 1;
|
||||
}
|
||||
}
|
||||
@ -2556,7 +2535,7 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
|
||||
#endif
|
||||
card = info->card;
|
||||
channel = (info->line) - (card->first_line);
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -2601,7 +2580,7 @@ static void cy_flush_buffer(struct tty_struct *tty)
|
||||
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
|
||||
spin_unlock_irqrestore(&card->card_lock, flags);
|
||||
|
||||
if (IS_CYC_Z(*card)) { /* If it is a Z card, flush the on-board
|
||||
if (cy_is_Z(card)) { /* If it is a Z card, flush the on-board
|
||||
buffers as well */
|
||||
spin_lock_irqsave(&card->card_lock, flags);
|
||||
retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L);
|
||||
@ -2682,7 +2661,7 @@ static void cy_close(struct tty_struct *tty, struct file *filp)
|
||||
|
||||
spin_lock_irqsave(&card->card_lock, flags);
|
||||
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
int channel = info->line - card->first_line;
|
||||
int index = card->bus_index;
|
||||
void __iomem *base_addr = card->base_addr +
|
||||
@ -2902,7 +2881,7 @@ static int cy_chars_in_buffer(struct tty_struct *tty)
|
||||
channel = (info->line) - (card->first_line);
|
||||
|
||||
#ifdef Z_EXT_CHARS_IN_BUFFER
|
||||
if (!IS_CYC_Z(cy_card[card])) {
|
||||
if (!cy_is_Z(card)) {
|
||||
#endif /* Z_EXT_CHARS_IN_BUFFER */
|
||||
#ifdef CY_DEBUG_IO
|
||||
printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
|
||||
@ -2984,7 +2963,6 @@ static void set_line_char(struct cyclades_port *info)
|
||||
void __iomem *base_addr;
|
||||
int chip, channel, index;
|
||||
unsigned cflag, iflag;
|
||||
unsigned short chip_number;
|
||||
int baud, baud_rate = 0;
|
||||
int i;
|
||||
|
||||
@ -3013,9 +2991,8 @@ static void set_line_char(struct cyclades_port *info)
|
||||
|
||||
card = info->card;
|
||||
channel = info->line - card->first_line;
|
||||
chip_number = channel / 4;
|
||||
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
|
||||
index = card->bus_index;
|
||||
|
||||
@ -3233,21 +3210,17 @@ static void set_line_char(struct cyclades_port *info)
|
||||
} else {
|
||||
struct FIRM_ID __iomem *firm_id;
|
||||
struct ZFW_CTRL __iomem *zfw_ctrl;
|
||||
struct BOARD_CTRL __iomem *board_ctrl;
|
||||
struct CH_CTRL __iomem *ch_ctrl;
|
||||
struct BUF_CTRL __iomem *buf_ctrl;
|
||||
__u32 sw_flow;
|
||||
int retval;
|
||||
|
||||
firm_id = card->base_addr + ID_ADDRESS;
|
||||
if (!ISZLOADED(*card))
|
||||
if (!cyz_is_loaded(card))
|
||||
return;
|
||||
|
||||
zfw_ctrl = card->base_addr +
|
||||
(readl(&firm_id->zfwctrl_addr) & 0xfffff);
|
||||
board_ctrl = &zfw_ctrl->board_ctrl;
|
||||
ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
|
||||
buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
|
||||
|
||||
/* baud rate */
|
||||
baud = tty_get_baud_rate(info->port.tty);
|
||||
@ -3457,7 +3430,7 @@ static int get_lsr_info(struct cyclades_port *info, unsigned int __user *value)
|
||||
|
||||
card = info->card;
|
||||
channel = (info->line) - (card->first_line);
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -3497,7 +3470,7 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file)
|
||||
|
||||
card = info->card;
|
||||
channel = info->line - card->first_line;
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -3523,7 +3496,7 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file)
|
||||
} else {
|
||||
base_addr = card->base_addr;
|
||||
firm_id = card->base_addr + ID_ADDRESS;
|
||||
if (ISZLOADED(*card)) {
|
||||
if (cyz_is_loaded(card)) {
|
||||
zfw_ctrl = card->base_addr +
|
||||
(readl(&firm_id->zfwctrl_addr) & 0xfffff);
|
||||
board_ctrl = &zfw_ctrl->board_ctrl;
|
||||
@ -3566,7 +3539,7 @@ cy_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
|
||||
card = info->card;
|
||||
channel = (info->line) - (card->first_line);
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -3641,7 +3614,7 @@ cy_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
base_addr = card->base_addr;
|
||||
|
||||
firm_id = card->base_addr + ID_ADDRESS;
|
||||
if (ISZLOADED(*card)) {
|
||||
if (cyz_is_loaded(card)) {
|
||||
zfw_ctrl = card->base_addr +
|
||||
(readl(&firm_id->zfwctrl_addr) & 0xfffff);
|
||||
board_ctrl = &zfw_ctrl->board_ctrl;
|
||||
@ -3713,7 +3686,7 @@ static int cy_break(struct tty_struct *tty, int break_state)
|
||||
card = info->card;
|
||||
|
||||
spin_lock_irqsave(&card->card_lock, flags);
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
/* Let the transmit ISR take care of this (since it
|
||||
requires stuffing characters into the output stream).
|
||||
*/
|
||||
@ -3782,7 +3755,7 @@ static int set_threshold(struct cyclades_port *info, unsigned long value)
|
||||
|
||||
card = info->card;
|
||||
channel = info->line - card->first_line;
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -3810,7 +3783,7 @@ static int get_threshold(struct cyclades_port *info,
|
||||
|
||||
card = info->card;
|
||||
channel = info->line - card->first_line;
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -3844,7 +3817,7 @@ static int set_timeout(struct cyclades_port *info, unsigned long value)
|
||||
|
||||
card = info->card;
|
||||
channel = info->line - card->first_line;
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -3867,7 +3840,7 @@ static int get_timeout(struct cyclades_port *info,
|
||||
|
||||
card = info->card;
|
||||
channel = info->line - card->first_line;
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -4121,7 +4094,7 @@ static void cy_send_xchar(struct tty_struct *tty, char ch)
|
||||
card = info->card;
|
||||
channel = info->line - card->first_line;
|
||||
|
||||
if (IS_CYC_Z(*card)) {
|
||||
if (cy_is_Z(card)) {
|
||||
if (ch == STOP_CHAR(tty))
|
||||
cyz_issue_cmd(card, channel, C_CM_SENDXOFF, 0L);
|
||||
else if (ch == START_CHAR(tty))
|
||||
@ -4154,7 +4127,7 @@ static void cy_throttle(struct tty_struct *tty)
|
||||
card = info->card;
|
||||
|
||||
if (I_IXOFF(tty)) {
|
||||
if (!IS_CYC_Z(*card))
|
||||
if (!cy_is_Z(card))
|
||||
cy_send_xchar(tty, STOP_CHAR(tty));
|
||||
else
|
||||
info->throttle = 1;
|
||||
@ -4162,7 +4135,7 @@ static void cy_throttle(struct tty_struct *tty)
|
||||
|
||||
if (tty->termios->c_cflag & CRTSCTS) {
|
||||
channel = info->line - card->first_line;
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -4219,7 +4192,7 @@ static void cy_unthrottle(struct tty_struct *tty)
|
||||
if (tty->termios->c_cflag & CRTSCTS) {
|
||||
card = info->card;
|
||||
channel = info->line - card->first_line;
|
||||
if (!IS_CYC_Z(*card)) {
|
||||
if (!cy_is_Z(card)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
index = card->bus_index;
|
||||
@ -4263,7 +4236,7 @@ static void cy_stop(struct tty_struct *tty)
|
||||
|
||||
cinfo = info->card;
|
||||
channel = info->line - cinfo->first_line;
|
||||
if (!IS_CYC_Z(*cinfo)) {
|
||||
if (!cy_is_Z(cinfo)) {
|
||||
index = cinfo->bus_index;
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
@ -4296,7 +4269,7 @@ static void cy_start(struct tty_struct *tty)
|
||||
cinfo = info->card;
|
||||
channel = info->line - cinfo->first_line;
|
||||
index = cinfo->bus_index;
|
||||
if (!IS_CYC_Z(*cinfo)) {
|
||||
if (!cy_is_Z(cinfo)) {
|
||||
chip = channel >> 2;
|
||||
channel &= 0x03;
|
||||
base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index);
|
||||
@ -4347,33 +4320,20 @@ static void cy_hangup(struct tty_struct *tty)
|
||||
static int __devinit cy_init_card(struct cyclades_card *cinfo)
|
||||
{
|
||||
struct cyclades_port *info;
|
||||
u32 uninitialized_var(mailbox);
|
||||
unsigned int nports, port;
|
||||
unsigned int port;
|
||||
unsigned short chip_number;
|
||||
int uninitialized_var(index);
|
||||
|
||||
spin_lock_init(&cinfo->card_lock);
|
||||
cinfo->intr_enabled = 0;
|
||||
|
||||
if (IS_CYC_Z(*cinfo)) { /* Cyclades-Z */
|
||||
mailbox = readl(&((struct RUNTIME_9060 __iomem *)
|
||||
cinfo->ctl_addr)->mail_box_0);
|
||||
nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8;
|
||||
cinfo->intr_enabled = 0;
|
||||
cinfo->nports = 0; /* Will be correctly set later, after
|
||||
Z FW is loaded */
|
||||
} else {
|
||||
index = cinfo->bus_index;
|
||||
nports = cinfo->nports = CyPORTS_PER_CHIP * cinfo->num_chips;
|
||||
}
|
||||
|
||||
cinfo->ports = kzalloc(sizeof(*cinfo->ports) * nports, GFP_KERNEL);
|
||||
cinfo->ports = kcalloc(cinfo->nports, sizeof(*cinfo->ports),
|
||||
GFP_KERNEL);
|
||||
if (cinfo->ports == NULL) {
|
||||
printk(KERN_ERR "Cyclades: cannot allocate ports\n");
|
||||
cinfo->nports = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (port = cinfo->first_line; port < cinfo->first_line + nports;
|
||||
for (port = cinfo->first_line; port < cinfo->first_line + cinfo->nports;
|
||||
port++) {
|
||||
info = &cinfo->ports[port - cinfo->first_line];
|
||||
tty_port_init(&info->port);
|
||||
@ -4387,9 +4347,9 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo)
|
||||
init_completion(&info->shutdown_wait);
|
||||
init_waitqueue_head(&info->delta_msr_wait);
|
||||
|
||||
if (IS_CYC_Z(*cinfo)) {
|
||||
if (cy_is_Z(cinfo)) {
|
||||
info->type = PORT_STARTECH;
|
||||
if (mailbox == ZO_V1)
|
||||
if (cinfo->hw_ver == ZO_V1)
|
||||
info->xmit_fifo_size = CYZ_FIFO_SIZE;
|
||||
else
|
||||
info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE;
|
||||
@ -4398,6 +4358,7 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo)
|
||||
cyz_rx_restart, (unsigned long)info);
|
||||
#endif
|
||||
} else {
|
||||
int index = cinfo->bus_index;
|
||||
info->type = PORT_CIRRUS;
|
||||
info->xmit_fifo_size = CyMAX_CHAR_FIFO;
|
||||
info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS;
|
||||
@ -4430,7 +4391,7 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo)
|
||||
}
|
||||
|
||||
#ifndef CONFIG_CYZ_INTR
|
||||
if (IS_CYC_Z(*cinfo) && !timer_pending(&cyz_timerlist)) {
|
||||
if (cy_is_Z(cinfo) && !timer_pending(&cyz_timerlist)) {
|
||||
mod_timer(&cyz_timerlist, jiffies + 1);
|
||||
#ifdef CY_PCI_DEBUG
|
||||
printk(KERN_DEBUG "Cyclades-Z polling initialized\n");
|
||||
@ -4621,11 +4582,12 @@ static int __init cy_detect_isa(void)
|
||||
|
||||
/* set cy_card */
|
||||
cy_card[j].base_addr = cy_isa_address;
|
||||
cy_card[j].ctl_addr = NULL;
|
||||
cy_card[j].ctl_addr.p9050 = NULL;
|
||||
cy_card[j].irq = (int)cy_isa_irq;
|
||||
cy_card[j].bus_index = 0;
|
||||
cy_card[j].first_line = cy_next_channel;
|
||||
cy_card[j].num_chips = cy_isa_nchan / 4;
|
||||
cy_card[j].num_chips = cy_isa_nchan / CyPORTS_PER_CHIP;
|
||||
cy_card[j].nports = cy_isa_nchan;
|
||||
if (cy_init_card(&cy_card[j])) {
|
||||
cy_card[j].base_addr = NULL;
|
||||
free_irq(cy_isa_irq, &cy_card[j]);
|
||||
@ -4781,7 +4743,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
|
||||
struct CUSTOM_REG __iomem *cust = base_addr;
|
||||
struct ZFW_CTRL __iomem *pt_zfwctrl;
|
||||
void __iomem *tmp;
|
||||
u32 mailbox, status;
|
||||
u32 mailbox, status, nchan;
|
||||
unsigned int i;
|
||||
int retval;
|
||||
|
||||
@ -4793,7 +4755,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
|
||||
|
||||
/* Check whether the firmware is already loaded and running. If
|
||||
positive, skip this board */
|
||||
if (Z_FPGA_LOADED(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) {
|
||||
if (__cyz_fpga_loaded(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) {
|
||||
u32 cntval = readl(base_addr + 0x190);
|
||||
|
||||
udelay(100);
|
||||
@ -4812,7 +4774,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
|
||||
|
||||
mailbox = readl(&ctl_addr->mail_box_0);
|
||||
|
||||
if (mailbox == 0 || Z_FPGA_LOADED(ctl_addr)) {
|
||||
if (mailbox == 0 || __cyz_fpga_loaded(ctl_addr)) {
|
||||
/* stops CPU and set window to beginning of RAM */
|
||||
cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
|
||||
cy_writel(&cust->cpu_stop, 0);
|
||||
@ -4828,7 +4790,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
|
||||
base_addr);
|
||||
if (retval)
|
||||
goto err_rel;
|
||||
if (!Z_FPGA_LOADED(ctl_addr)) {
|
||||
if (!__cyz_fpga_loaded(ctl_addr)) {
|
||||
dev_err(&pdev->dev, "fw upload successful, but fw is "
|
||||
"not loaded\n");
|
||||
goto err_rel;
|
||||
@ -4887,7 +4849,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
|
||||
"system before loading the new FW to the "
|
||||
"Cyclades-Z.\n");
|
||||
|
||||
if (Z_FPGA_LOADED(ctl_addr))
|
||||
if (__cyz_fpga_loaded(ctl_addr))
|
||||
plx_init(pdev, irq, ctl_addr);
|
||||
|
||||
retval = -EIO;
|
||||
@ -4902,16 +4864,16 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
|
||||
base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr),
|
||||
base_addr + readl(&fid->zfwctrl_addr));
|
||||
|
||||
nchan = readl(&pt_zfwctrl->board_ctrl.n_channel);
|
||||
dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n",
|
||||
readl(&pt_zfwctrl->board_ctrl.fw_version),
|
||||
readl(&pt_zfwctrl->board_ctrl.n_channel));
|
||||
readl(&pt_zfwctrl->board_ctrl.fw_version), nchan);
|
||||
|
||||
if (readl(&pt_zfwctrl->board_ctrl.n_channel) == 0) {
|
||||
if (nchan == 0) {
|
||||
dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please "
|
||||
"check the connection between the Z host card and the "
|
||||
"serial expanders.\n");
|
||||
|
||||
if (Z_FPGA_LOADED(ctl_addr))
|
||||
if (__cyz_fpga_loaded(ctl_addr))
|
||||
plx_init(pdev, irq, ctl_addr);
|
||||
|
||||
dev_info(&pdev->dev, "Null number of ports detected. Board "
|
||||
@ -4932,9 +4894,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
|
||||
cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
|
||||
0x00030800UL);
|
||||
|
||||
plx_init(pdev, irq, ctl_addr);
|
||||
|
||||
return 0;
|
||||
return nchan;
|
||||
err_rel:
|
||||
release_firmware(fw);
|
||||
err:
|
||||
@ -4946,7 +4906,7 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
|
||||
{
|
||||
void __iomem *addr0 = NULL, *addr2 = NULL;
|
||||
char *card_name = NULL;
|
||||
u32 mailbox;
|
||||
u32 uninitialized_var(mailbox);
|
||||
unsigned int device_id, nchan = 0, card_no, i;
|
||||
unsigned char plx_ver;
|
||||
int retval, irq;
|
||||
@ -5023,11 +4983,12 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
|
||||
}
|
||||
|
||||
/* Disable interrupts on the PLX before resetting it */
|
||||
cy_writew(addr0 + 0x68, readw(addr0 + 0x68) & ~0x0900);
|
||||
cy_writew(&ctl_addr->intr_ctrl_stat,
|
||||
readw(&ctl_addr->intr_ctrl_stat) & ~0x0900);
|
||||
|
||||
plx_init(pdev, irq, addr0);
|
||||
|
||||
mailbox = (u32)readl(&ctl_addr->mail_box_0);
|
||||
mailbox = readl(&ctl_addr->mail_box_0);
|
||||
|
||||
addr2 = ioremap_nocache(pci_resource_start(pdev, 2),
|
||||
mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin);
|
||||
@ -5038,12 +4999,8 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
|
||||
|
||||
if (mailbox == ZE_V1) {
|
||||
card_name = "Cyclades-Ze";
|
||||
|
||||
readl(&ctl_addr->mail_box_0);
|
||||
nchan = ZE_V1_NPORTS;
|
||||
} else {
|
||||
card_name = "Cyclades-8Zo";
|
||||
|
||||
#ifdef CY_PCI_DEBUG
|
||||
if (mailbox == ZO_V1) {
|
||||
cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
|
||||
@ -5065,15 +5022,12 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
|
||||
*/
|
||||
if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
|
||||
cy_writel(addr2 + ID_ADDRESS, 0L);
|
||||
|
||||
retval = cyz_load_fw(pdev, addr2, addr0, irq);
|
||||
if (retval)
|
||||
goto err_unmap;
|
||||
/* This must be a Cyclades-8Zo/PCI. The extendable
|
||||
version will have a different device_id and will
|
||||
be allocated its maximum number of ports. */
|
||||
nchan = 8;
|
||||
}
|
||||
|
||||
retval = cyz_load_fw(pdev, addr2, addr0, irq);
|
||||
if (retval <= 0)
|
||||
goto err_unmap;
|
||||
nchan = retval;
|
||||
}
|
||||
|
||||
if ((cy_next_channel + nchan) > NR_PORTS) {
|
||||
@ -5103,8 +5057,10 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
|
||||
dev_err(&pdev->dev, "could not allocate IRQ\n");
|
||||
goto err_unmap;
|
||||
}
|
||||
cy_card[card_no].num_chips = nchan / 4;
|
||||
cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP;
|
||||
} else {
|
||||
cy_card[card_no].hw_ver = mailbox;
|
||||
cy_card[card_no].num_chips = (unsigned int)-1;
|
||||
#ifdef CONFIG_CYZ_INTR
|
||||
/* allocate IRQ only if board has an IRQ */
|
||||
if (irq != 0 && irq != 255) {
|
||||
@ -5117,15 +5073,15 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_CYZ_INTR */
|
||||
cy_card[card_no].num_chips = (unsigned int)-1;
|
||||
}
|
||||
|
||||
/* set cy_card */
|
||||
cy_card[card_no].base_addr = addr2;
|
||||
cy_card[card_no].ctl_addr = addr0;
|
||||
cy_card[card_no].ctl_addr.p9050 = addr0;
|
||||
cy_card[card_no].irq = irq;
|
||||
cy_card[card_no].bus_index = 1;
|
||||
cy_card[card_no].first_line = cy_next_channel;
|
||||
cy_card[card_no].nports = nchan;
|
||||
retval = cy_init_card(&cy_card[card_no]);
|
||||
if (retval)
|
||||
goto err_null;
|
||||
@ -5138,17 +5094,20 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
|
||||
plx_ver = readb(addr2 + CyPLX_VER) & 0x0f;
|
||||
switch (plx_ver) {
|
||||
case PLX_9050:
|
||||
|
||||
cy_writeb(addr0 + 0x4c, 0x43);
|
||||
break;
|
||||
|
||||
case PLX_9060:
|
||||
case PLX_9080:
|
||||
default: /* Old boards, use PLX_9060 */
|
||||
plx_init(pdev, irq, addr0);
|
||||
cy_writew(addr0 + 0x68, readw(addr0 + 0x68) | 0x0900);
|
||||
{
|
||||
struct RUNTIME_9060 __iomem *ctl_addr = addr0;
|
||||
plx_init(pdev, irq, ctl_addr);
|
||||
cy_writew(&ctl_addr->intr_ctrl_stat,
|
||||
readw(&ctl_addr->intr_ctrl_stat) | 0x0900);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from "
|
||||
@ -5179,22 +5138,23 @@ static void __devexit cy_pci_remove(struct pci_dev *pdev)
|
||||
unsigned int i;
|
||||
|
||||
/* non-Z with old PLX */
|
||||
if (!IS_CYC_Z(*cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) ==
|
||||
if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) ==
|
||||
PLX_9050)
|
||||
cy_writeb(cinfo->ctl_addr + 0x4c, 0);
|
||||
cy_writeb(cinfo->ctl_addr.p9050 + 0x4c, 0);
|
||||
else
|
||||
#ifndef CONFIG_CYZ_INTR
|
||||
if (!IS_CYC_Z(*cinfo))
|
||||
if (!cy_is_Z(cinfo))
|
||||
#endif
|
||||
cy_writew(cinfo->ctl_addr + 0x68,
|
||||
readw(cinfo->ctl_addr + 0x68) & ~0x0900);
|
||||
cy_writew(&cinfo->ctl_addr.p9060->intr_ctrl_stat,
|
||||
readw(&cinfo->ctl_addr.p9060->intr_ctrl_stat) &
|
||||
~0x0900);
|
||||
|
||||
iounmap(cinfo->base_addr);
|
||||
if (cinfo->ctl_addr)
|
||||
iounmap(cinfo->ctl_addr);
|
||||
if (cinfo->ctl_addr.p9050)
|
||||
iounmap(cinfo->ctl_addr.p9050);
|
||||
if (cinfo->irq
|
||||
#ifndef CONFIG_CYZ_INTR
|
||||
&& !IS_CYC_Z(*cinfo)
|
||||
&& !cy_is_Z(cinfo)
|
||||
#endif /* CONFIG_CYZ_INTR */
|
||||
)
|
||||
free_irq(cinfo->irq, cinfo);
|
||||
@ -5240,7 +5200,7 @@ static int cyclades_proc_show(struct seq_file *m, void *v)
|
||||
(cur_jifs - info->idle_stats.recv_idle)/
|
||||
HZ, info->idle_stats.overruns,
|
||||
/* FIXME: double check locking */
|
||||
(long)info->port.tty->ldisc.ops->num);
|
||||
(long)info->port.tty->ldisc->ops->num);
|
||||
else
|
||||
seq_printf(m, "%3d %8lu %10lu %8lu "
|
||||
"%10lu %8lu %9lu %6ld\n",
|
||||
@ -5386,11 +5346,11 @@ static void __exit cy_cleanup_module(void)
|
||||
/* clear interrupt */
|
||||
cy_writeb(card->base_addr + Cy_ClrIntr, 0);
|
||||
iounmap(card->base_addr);
|
||||
if (card->ctl_addr)
|
||||
iounmap(card->ctl_addr);
|
||||
if (card->ctl_addr.p9050)
|
||||
iounmap(card->ctl_addr.p9050);
|
||||
if (card->irq
|
||||
#ifndef CONFIG_CYZ_INTR
|
||||
&& !IS_CYC_Z(*card)
|
||||
&& !cy_is_Z(card)
|
||||
#endif /* CONFIG_CYZ_INTR */
|
||||
)
|
||||
free_irq(card->irq, card);
|
||||
|
@ -745,7 +745,7 @@ static int epca_carrier_raised(struct tty_port *port)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void epca_raise_dtr_rts(struct tty_port *port)
|
||||
static void epca_dtr_rts(struct tty_port *port, int onoff)
|
||||
{
|
||||
}
|
||||
|
||||
@ -925,7 +925,7 @@ static const struct tty_operations pc_ops = {
|
||||
|
||||
static const struct tty_port_operations epca_port_ops = {
|
||||
.carrier_raised = epca_carrier_raised,
|
||||
.raise_dtr_rts = epca_raise_dtr_rts,
|
||||
.dtr_rts = epca_dtr_rts,
|
||||
};
|
||||
|
||||
static int info_open(struct tty_struct *tty, struct file *filp)
|
||||
@ -1518,7 +1518,7 @@ static void doevent(int crd)
|
||||
if (event & MODEMCHG_IND) {
|
||||
/* A modem signal change has been indicated */
|
||||
ch->imodem = mstat;
|
||||
if (test_bit(ASYNC_CHECK_CD, &ch->port.flags)) {
|
||||
if (test_bit(ASYNCB_CHECK_CD, &ch->port.flags)) {
|
||||
/* We are now receiving dcd */
|
||||
if (mstat & ch->dcd)
|
||||
wake_up_interruptible(&ch->port.open_wait);
|
||||
@ -1765,9 +1765,9 @@ static void epcaparam(struct tty_struct *tty, struct channel *ch)
|
||||
* that the driver will wait on carrier detect.
|
||||
*/
|
||||
if (ts->c_cflag & CLOCAL)
|
||||
clear_bit(ASYNC_CHECK_CD, &ch->port.flags);
|
||||
clear_bit(ASYNCB_CHECK_CD, &ch->port.flags);
|
||||
else
|
||||
set_bit(ASYNC_CHECK_CD, &ch->port.flags);
|
||||
set_bit(ASYNCB_CHECK_CD, &ch->port.flags);
|
||||
mval = ch->m_dtr | ch->m_rts;
|
||||
} /* End CBAUD not detected */
|
||||
iflag = termios2digi_i(ch, ts->c_iflag);
|
||||
@ -2114,8 +2114,8 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file,
|
||||
tty_wait_until_sent(tty, 0);
|
||||
} else {
|
||||
/* ldisc lock already held in ioctl */
|
||||
if (tty->ldisc.ops->flush_buffer)
|
||||
tty->ldisc.ops->flush_buffer(tty);
|
||||
if (tty->ldisc->ops->flush_buffer)
|
||||
tty->ldisc->ops->flush_buffer(tty);
|
||||
}
|
||||
unlock_kernel();
|
||||
/* Fall Thru */
|
||||
@ -2244,7 +2244,8 @@ static void do_softint(struct work_struct *work)
|
||||
if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) {
|
||||
tty_hangup(tty);
|
||||
wake_up_interruptible(&ch->port.open_wait);
|
||||
clear_bit(ASYNC_NORMAL_ACTIVE, &ch->port.flags);
|
||||
clear_bit(ASYNCB_NORMAL_ACTIVE,
|
||||
&ch->port.flags);
|
||||
}
|
||||
}
|
||||
tty_kref_put(tty);
|
||||
|
@ -868,11 +868,11 @@ i2Input(i2ChanStrPtr pCh)
|
||||
amountToMove = count;
|
||||
}
|
||||
// Move the first block
|
||||
pCh->pTTY->ldisc.ops->receive_buf( pCh->pTTY,
|
||||
pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY,
|
||||
&(pCh->Ibuf[stripIndex]), NULL, amountToMove );
|
||||
// If we needed to wrap, do the second data move
|
||||
if (count > amountToMove) {
|
||||
pCh->pTTY->ldisc.ops->receive_buf( pCh->pTTY,
|
||||
pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY,
|
||||
pCh->Ibuf, NULL, count - amountToMove );
|
||||
}
|
||||
// Bump and wrap the stripIndex all at once by the amount of data read. This
|
||||
|
@ -1315,8 +1315,8 @@ static inline void isig(int sig, struct tty_struct *tty, int flush)
|
||||
if (tty->pgrp)
|
||||
kill_pgrp(tty->pgrp, sig, 1);
|
||||
if (flush || !L_NOFLSH(tty)) {
|
||||
if ( tty->ldisc.ops->flush_buffer )
|
||||
tty->ldisc.ops->flush_buffer(tty);
|
||||
if ( tty->ldisc->ops->flush_buffer )
|
||||
tty->ldisc->ops->flush_buffer(tty);
|
||||
i2InputFlush( tty->driver_data );
|
||||
}
|
||||
}
|
||||
|
@ -329,7 +329,7 @@ static inline void drop_rts(struct isi_port *port)
|
||||
|
||||
/* card->lock MUST NOT be held */
|
||||
|
||||
static void isicom_raise_dtr_rts(struct tty_port *port)
|
||||
static void isicom_dtr_rts(struct tty_port *port, int on)
|
||||
{
|
||||
struct isi_port *ip = container_of(port, struct isi_port, port);
|
||||
struct isi_board *card = ip->card;
|
||||
@ -339,10 +339,17 @@ static void isicom_raise_dtr_rts(struct tty_port *port)
|
||||
if (!lock_card(card))
|
||||
return;
|
||||
|
||||
outw(0x8000 | (channel << card->shift_count) | 0x02, base);
|
||||
outw(0x0f04, base);
|
||||
InterruptTheCard(base);
|
||||
ip->status |= (ISI_DTR | ISI_RTS);
|
||||
if (on) {
|
||||
outw(0x8000 | (channel << card->shift_count) | 0x02, base);
|
||||
outw(0x0f04, base);
|
||||
InterruptTheCard(base);
|
||||
ip->status |= (ISI_DTR | ISI_RTS);
|
||||
} else {
|
||||
outw(0x8000 | (channel << card->shift_count) | 0x02, base);
|
||||
outw(0x0C04, base);
|
||||
InterruptTheCard(base);
|
||||
ip->status &= ~(ISI_DTR | ISI_RTS);
|
||||
}
|
||||
unlock_card(card);
|
||||
}
|
||||
|
||||
@ -1339,7 +1346,7 @@ static const struct tty_operations isicom_ops = {
|
||||
|
||||
static const struct tty_port_operations isicom_port_ops = {
|
||||
.carrier_raised = isicom_carrier_raised,
|
||||
.raise_dtr_rts = isicom_raise_dtr_rts,
|
||||
.dtr_rts = isicom_dtr_rts,
|
||||
};
|
||||
|
||||
static int __devinit reset_card(struct pci_dev *pdev,
|
||||
|
@ -1140,14 +1140,14 @@ static int stli_carrier_raised(struct tty_port *port)
|
||||
return (portp->sigs & TIOCM_CD) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void stli_raise_dtr_rts(struct tty_port *port)
|
||||
static void stli_dtr_rts(struct tty_port *port, int on)
|
||||
{
|
||||
struct stliport *portp = container_of(port, struct stliport, port);
|
||||
struct stlibrd *brdp = stli_brds[portp->brdnr];
|
||||
stli_mkasysigs(&portp->asig, 1, 1);
|
||||
stli_mkasysigs(&portp->asig, on, on);
|
||||
if (stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
|
||||
sizeof(asysigs_t), 0) < 0)
|
||||
printk(KERN_WARNING "istallion: dtr raise failed.\n");
|
||||
printk(KERN_WARNING "istallion: dtr set failed.\n");
|
||||
}
|
||||
|
||||
|
||||
@ -4417,7 +4417,7 @@ static const struct tty_operations stli_ops = {
|
||||
|
||||
static const struct tty_port_operations stli_port_ops = {
|
||||
.carrier_raised = stli_carrier_raised,
|
||||
.raise_dtr_rts = stli_raise_dtr_rts,
|
||||
.dtr_rts = stli_dtr_rts,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -1184,6 +1184,11 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (port % MAX_PORTS_PER_BOARD >= brd->numPorts) {
|
||||
mutex_unlock(&moxa_openlock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
|
||||
ch->port.count++;
|
||||
tty->driver_data = ch;
|
||||
|
@ -547,14 +547,18 @@ static int mxser_carrier_raised(struct tty_port *port)
|
||||
return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0;
|
||||
}
|
||||
|
||||
static void mxser_raise_dtr_rts(struct tty_port *port)
|
||||
static void mxser_dtr_rts(struct tty_port *port, int on)
|
||||
{
|
||||
struct mxser_port *mp = container_of(port, struct mxser_port, port);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mp->slock, flags);
|
||||
outb(inb(mp->ioaddr + UART_MCR) |
|
||||
UART_MCR_DTR | UART_MCR_RTS, mp->ioaddr + UART_MCR);
|
||||
if (on)
|
||||
outb(inb(mp->ioaddr + UART_MCR) |
|
||||
UART_MCR_DTR | UART_MCR_RTS, mp->ioaddr + UART_MCR);
|
||||
else
|
||||
outb(inb(mp->ioaddr + UART_MCR)&~(UART_MCR_DTR | UART_MCR_RTS),
|
||||
mp->ioaddr + UART_MCR);
|
||||
spin_unlock_irqrestore(&mp->slock, flags);
|
||||
}
|
||||
|
||||
@ -2356,7 +2360,7 @@ static const struct tty_operations mxser_ops = {
|
||||
|
||||
struct tty_port_operations mxser_port_ops = {
|
||||
.carrier_raised = mxser_carrier_raised,
|
||||
.raise_dtr_rts = mxser_raise_dtr_rts,
|
||||
.dtr_rts = mxser_dtr_rts,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -342,8 +342,8 @@ static int n_hdlc_tty_open (struct tty_struct *tty)
|
||||
#endif
|
||||
|
||||
/* Flush any pending characters in the driver and discipline. */
|
||||
if (tty->ldisc.ops->flush_buffer)
|
||||
tty->ldisc.ops->flush_buffer(tty);
|
||||
if (tty->ldisc->ops->flush_buffer)
|
||||
tty->ldisc->ops->flush_buffer(tty);
|
||||
|
||||
tty_driver_flush_buffer(tty);
|
||||
|
||||
|
@ -73,24 +73,6 @@
|
||||
#define ECHO_OP_SET_CANON_COL 0x81
|
||||
#define ECHO_OP_ERASE_TAB 0x82
|
||||
|
||||
static inline unsigned char *alloc_buf(void)
|
||||
{
|
||||
gfp_t prio = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
|
||||
|
||||
if (PAGE_SIZE != N_TTY_BUF_SIZE)
|
||||
return kmalloc(N_TTY_BUF_SIZE, prio);
|
||||
else
|
||||
return (unsigned char *)__get_free_page(prio);
|
||||
}
|
||||
|
||||
static inline void free_buf(unsigned char *buf)
|
||||
{
|
||||
if (PAGE_SIZE != N_TTY_BUF_SIZE)
|
||||
kfree(buf);
|
||||
else
|
||||
free_page((unsigned long) buf);
|
||||
}
|
||||
|
||||
static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
|
||||
unsigned char __user *ptr)
|
||||
{
|
||||
@ -1558,11 +1540,11 @@ static void n_tty_close(struct tty_struct *tty)
|
||||
{
|
||||
n_tty_flush_buffer(tty);
|
||||
if (tty->read_buf) {
|
||||
free_buf(tty->read_buf);
|
||||
kfree(tty->read_buf);
|
||||
tty->read_buf = NULL;
|
||||
}
|
||||
if (tty->echo_buf) {
|
||||
free_buf(tty->echo_buf);
|
||||
kfree(tty->echo_buf);
|
||||
tty->echo_buf = NULL;
|
||||
}
|
||||
}
|
||||
@ -1584,17 +1566,16 @@ static int n_tty_open(struct tty_struct *tty)
|
||||
|
||||
/* These are ugly. Currently a malloc failure here can panic */
|
||||
if (!tty->read_buf) {
|
||||
tty->read_buf = alloc_buf();
|
||||
tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
|
||||
if (!tty->read_buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!tty->echo_buf) {
|
||||
tty->echo_buf = alloc_buf();
|
||||
tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
|
||||
|
||||
if (!tty->echo_buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
|
||||
memset(tty->echo_buf, 0, N_TTY_BUF_SIZE);
|
||||
reset_buffer_flags(tty);
|
||||
tty->column = 0;
|
||||
n_tty_set_termios(tty, NULL);
|
||||
|
@ -383,7 +383,7 @@ static void async_mode(MGSLPC_INFO *info);
|
||||
static void tx_timeout(unsigned long context);
|
||||
|
||||
static int carrier_raised(struct tty_port *port);
|
||||
static void raise_dtr_rts(struct tty_port *port);
|
||||
static void dtr_rts(struct tty_port *port, int onoff);
|
||||
|
||||
#if SYNCLINK_GENERIC_HDLC
|
||||
#define dev_to_port(D) (dev_to_hdlc(D)->priv)
|
||||
@ -513,7 +513,7 @@ static void ldisc_receive_buf(struct tty_struct *tty,
|
||||
|
||||
static const struct tty_port_operations mgslpc_port_ops = {
|
||||
.carrier_raised = carrier_raised,
|
||||
.raise_dtr_rts = raise_dtr_rts
|
||||
.dtr_rts = dtr_rts
|
||||
};
|
||||
|
||||
static int mgslpc_probe(struct pcmcia_device *link)
|
||||
@ -2528,13 +2528,16 @@ static int carrier_raised(struct tty_port *port)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void raise_dtr_rts(struct tty_port *port)
|
||||
static void dtr_rts(struct tty_port *port, int onoff)
|
||||
{
|
||||
MGSLPC_INFO *info = container_of(port, MGSLPC_INFO, port);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&info->lock,flags);
|
||||
info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
|
||||
if (onoff)
|
||||
info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
|
||||
else
|
||||
info->serial_signals &= ~SerialSignal_RTS + SerialSignal_DTR;
|
||||
set_signals(info);
|
||||
spin_unlock_irqrestore(&info->lock,flags);
|
||||
}
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
#include <asm/system.h>
|
||||
|
||||
/* These are global because they are accessed in tty_io.c */
|
||||
#ifdef CONFIG_UNIX98_PTYS
|
||||
static struct tty_driver *ptm_driver;
|
||||
static struct tty_driver *pts_driver;
|
||||
@ -111,7 +110,7 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf,
|
||||
c = to->receive_room;
|
||||
if (c > count)
|
||||
c = count;
|
||||
to->ldisc.ops->receive_buf(to, buf, NULL, c);
|
||||
to->ldisc->ops->receive_buf(to, buf, NULL, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
@ -149,11 +148,11 @@ static int pty_chars_in_buffer(struct tty_struct *tty)
|
||||
int count;
|
||||
|
||||
/* We should get the line discipline lock for "tty->link" */
|
||||
if (!to || !to->ldisc.ops->chars_in_buffer)
|
||||
if (!to || !to->ldisc->ops->chars_in_buffer)
|
||||
return 0;
|
||||
|
||||
/* The ldisc must report 0 if no characters available to be read */
|
||||
count = to->ldisc.ops->chars_in_buffer(to);
|
||||
count = to->ldisc->ops->chars_in_buffer(to);
|
||||
|
||||
if (tty->driver->subtype == PTY_TYPE_SLAVE)
|
||||
return count;
|
||||
@ -187,8 +186,8 @@ static void pty_flush_buffer(struct tty_struct *tty)
|
||||
if (!to)
|
||||
return;
|
||||
|
||||
if (to->ldisc.ops->flush_buffer)
|
||||
to->ldisc.ops->flush_buffer(to);
|
||||
if (to->ldisc->ops->flush_buffer)
|
||||
to->ldisc->ops->flush_buffer(to);
|
||||
|
||||
if (to->packet) {
|
||||
spin_lock_irqsave(&tty->ctrl_lock, flags);
|
||||
|
@ -872,11 +872,16 @@ static int carrier_raised(struct tty_port *port)
|
||||
return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void raise_dtr_rts(struct tty_port *port)
|
||||
static void dtr_rts(struct tty_port *port, int on)
|
||||
{
|
||||
struct r_port *info = container_of(port, struct r_port, port);
|
||||
sSetDTR(&info->channel);
|
||||
sSetRTS(&info->channel);
|
||||
if (on) {
|
||||
sSetDTR(&info->channel);
|
||||
sSetRTS(&info->channel);
|
||||
} else {
|
||||
sClrDTR(&info->channel);
|
||||
sClrRTS(&info->channel);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -934,7 +939,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp)
|
||||
/*
|
||||
* Info->count is now 1; so it's safe to sleep now.
|
||||
*/
|
||||
if (!test_bit(ASYNC_INITIALIZED, &port->flags)) {
|
||||
if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) {
|
||||
cp = &info->channel;
|
||||
sSetRxTrigger(cp, TRIG_1);
|
||||
if (sGetChanStatus(cp) & CD_ACT)
|
||||
@ -958,7 +963,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp)
|
||||
sEnRxFIFO(cp);
|
||||
sEnTransmit(cp);
|
||||
|
||||
set_bit(ASYNC_INITIALIZED, &info->port.flags);
|
||||
set_bit(ASYNCB_INITIALIZED, &info->port.flags);
|
||||
|
||||
/*
|
||||
* Set up the tty->alt_speed kludge
|
||||
@ -1641,7 +1646,7 @@ static int rp_write(struct tty_struct *tty,
|
||||
/* Write remaining data into the port's xmit_buf */
|
||||
while (1) {
|
||||
/* Hung up ? */
|
||||
if (!test_bit(ASYNC_NORMAL_ACTIVE, &info->port.flags))
|
||||
if (!test_bit(ASYNCB_NORMAL_ACTIVE, &info->port.flags))
|
||||
goto end;
|
||||
c = min(count, XMIT_BUF_SIZE - info->xmit_cnt - 1);
|
||||
c = min(c, XMIT_BUF_SIZE - info->xmit_head);
|
||||
@ -2250,7 +2255,7 @@ static const struct tty_operations rocket_ops = {
|
||||
|
||||
static const struct tty_port_operations rocket_port_ops = {
|
||||
.carrier_raised = carrier_raised,
|
||||
.raise_dtr_rts = raise_dtr_rts,
|
||||
.dtr_rts = dtr_rts,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -327,7 +327,7 @@ int paste_selection(struct tty_struct *tty)
|
||||
}
|
||||
count = sel_buffer_lth - pasted;
|
||||
count = min(count, tty->receive_room);
|
||||
tty->ldisc.ops->receive_buf(tty, sel_buffer + pasted,
|
||||
tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted,
|
||||
NULL, count);
|
||||
pasted += count;
|
||||
}
|
||||
|
@ -772,11 +772,11 @@ static int stl_carrier_raised(struct tty_port *port)
|
||||
return (portp->sigs & TIOCM_CD) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void stl_raise_dtr_rts(struct tty_port *port)
|
||||
static void stl_dtr_rts(struct tty_port *port, int on)
|
||||
{
|
||||
struct stlport *portp = container_of(port, struct stlport, port);
|
||||
/* Takes brd_lock internally */
|
||||
stl_setsignals(portp, 1, 1);
|
||||
stl_setsignals(portp, on, on);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -2547,7 +2547,7 @@ static const struct tty_operations stl_ops = {
|
||||
|
||||
static const struct tty_port_operations stl_port_ops = {
|
||||
.carrier_raised = stl_carrier_raised,
|
||||
.raise_dtr_rts = stl_raise_dtr_rts,
|
||||
.dtr_rts = stl_dtr_rts,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -3247,13 +3247,16 @@ static int carrier_raised(struct tty_port *port)
|
||||
return (info->serial_signals & SerialSignal_DCD) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void raise_dtr_rts(struct tty_port *port)
|
||||
static void dtr_rts(struct tty_port *port, int on)
|
||||
{
|
||||
struct mgsl_struct *info = container_of(port, struct mgsl_struct, port);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&info->irq_spinlock,flags);
|
||||
info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
|
||||
if (on)
|
||||
info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
|
||||
else
|
||||
info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
|
||||
usc_set_serial_signals(info);
|
||||
spin_unlock_irqrestore(&info->irq_spinlock,flags);
|
||||
}
|
||||
@ -4258,7 +4261,7 @@ static void mgsl_add_device( struct mgsl_struct *info )
|
||||
|
||||
static const struct tty_port_operations mgsl_port_ops = {
|
||||
.carrier_raised = carrier_raised,
|
||||
.raise_dtr_rts = raise_dtr_rts,
|
||||
.dtr_rts = dtr_rts,
|
||||
};
|
||||
|
||||
|
||||
|
@ -214,6 +214,7 @@ struct slgt_desc
|
||||
#define set_desc_next(a,b) (a).next = cpu_to_le32((unsigned int)(b))
|
||||
#define set_desc_count(a,b)(a).count = cpu_to_le16((unsigned short)(b))
|
||||
#define set_desc_eof(a,b) (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0))
|
||||
#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b))
|
||||
#define desc_count(a) (le16_to_cpu((a).count))
|
||||
#define desc_status(a) (le16_to_cpu((a).status))
|
||||
#define desc_complete(a) (le16_to_cpu((a).status) & BIT15)
|
||||
@ -297,6 +298,7 @@ struct slgt_info {
|
||||
u32 max_frame_size; /* as set by device config */
|
||||
|
||||
unsigned int rbuf_fill_level;
|
||||
unsigned int rx_pio;
|
||||
unsigned int if_mode;
|
||||
unsigned int base_clock;
|
||||
|
||||
@ -331,6 +333,8 @@ struct slgt_info {
|
||||
struct slgt_desc *rbufs;
|
||||
unsigned int rbuf_current;
|
||||
unsigned int rbuf_index;
|
||||
unsigned int rbuf_fill_index;
|
||||
unsigned short rbuf_fill_count;
|
||||
|
||||
unsigned int tbuf_count;
|
||||
struct slgt_desc *tbufs;
|
||||
@ -2110,6 +2114,40 @@ static void ri_change(struct slgt_info *info, unsigned short status)
|
||||
info->pending_bh |= BH_STATUS;
|
||||
}
|
||||
|
||||
static void isr_rxdata(struct slgt_info *info)
|
||||
{
|
||||
unsigned int count = info->rbuf_fill_count;
|
||||
unsigned int i = info->rbuf_fill_index;
|
||||
unsigned short reg;
|
||||
|
||||
while (rd_reg16(info, SSR) & IRQ_RXDATA) {
|
||||
reg = rd_reg16(info, RDR);
|
||||
DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg));
|
||||
if (desc_complete(info->rbufs[i])) {
|
||||
/* all buffers full */
|
||||
rx_stop(info);
|
||||
info->rx_restart = 1;
|
||||
continue;
|
||||
}
|
||||
info->rbufs[i].buf[count++] = (unsigned char)reg;
|
||||
/* async mode saves status byte to buffer for each data byte */
|
||||
if (info->params.mode == MGSL_MODE_ASYNC)
|
||||
info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8);
|
||||
if (count == info->rbuf_fill_level || (reg & BIT10)) {
|
||||
/* buffer full or end of frame */
|
||||
set_desc_count(info->rbufs[i], count);
|
||||
set_desc_status(info->rbufs[i], BIT15 | (reg >> 8));
|
||||
info->rbuf_fill_count = count = 0;
|
||||
if (++i == info->rbuf_count)
|
||||
i = 0;
|
||||
info->pending_bh |= BH_RECEIVE;
|
||||
}
|
||||
}
|
||||
|
||||
info->rbuf_fill_index = i;
|
||||
info->rbuf_fill_count = count;
|
||||
}
|
||||
|
||||
static void isr_serial(struct slgt_info *info)
|
||||
{
|
||||
unsigned short status = rd_reg16(info, SSR);
|
||||
@ -2125,6 +2163,8 @@ static void isr_serial(struct slgt_info *info)
|
||||
if (info->tx_count)
|
||||
isr_txeom(info, status);
|
||||
}
|
||||
if (info->rx_pio && (status & IRQ_RXDATA))
|
||||
isr_rxdata(info);
|
||||
if ((status & IRQ_RXBREAK) && (status & RXBREAK)) {
|
||||
info->icount.brk++;
|
||||
/* process break detection if tty control allows */
|
||||
@ -2141,7 +2181,8 @@ static void isr_serial(struct slgt_info *info)
|
||||
} else {
|
||||
if (status & (IRQ_TXIDLE + IRQ_TXUNDER))
|
||||
isr_txeom(info, status);
|
||||
|
||||
if (info->rx_pio && (status & IRQ_RXDATA))
|
||||
isr_rxdata(info);
|
||||
if (status & IRQ_RXIDLE) {
|
||||
if (status & RXIDLE)
|
||||
info->icount.rxidle++;
|
||||
@ -2642,6 +2683,10 @@ static int rx_enable(struct slgt_info *info, int enable)
|
||||
return -EINVAL;
|
||||
}
|
||||
info->rbuf_fill_level = rbuf_fill_level;
|
||||
if (rbuf_fill_level < 128)
|
||||
info->rx_pio = 1; /* PIO mode */
|
||||
else
|
||||
info->rx_pio = 0; /* DMA mode */
|
||||
rx_stop(info); /* restart receiver to use new fill level */
|
||||
}
|
||||
|
||||
@ -3099,13 +3144,16 @@ static int carrier_raised(struct tty_port *port)
|
||||
return (info->signals & SerialSignal_DCD) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void raise_dtr_rts(struct tty_port *port)
|
||||
static void dtr_rts(struct tty_port *port, int on)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct slgt_info *info = container_of(port, struct slgt_info, port);
|
||||
|
||||
spin_lock_irqsave(&info->lock,flags);
|
||||
info->signals |= SerialSignal_RTS + SerialSignal_DTR;
|
||||
if (on)
|
||||
info->signals |= SerialSignal_RTS + SerialSignal_DTR;
|
||||
else
|
||||
info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
|
||||
set_signals(info);
|
||||
spin_unlock_irqrestore(&info->lock,flags);
|
||||
}
|
||||
@ -3419,7 +3467,7 @@ static void add_device(struct slgt_info *info)
|
||||
|
||||
static const struct tty_port_operations slgt_port_ops = {
|
||||
.carrier_raised = carrier_raised,
|
||||
.raise_dtr_rts = raise_dtr_rts,
|
||||
.dtr_rts = dtr_rts,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -3841,15 +3889,27 @@ static void rx_start(struct slgt_info *info)
|
||||
rdma_reset(info);
|
||||
reset_rbufs(info);
|
||||
|
||||
/* set 1st descriptor address */
|
||||
wr_reg32(info, RDDAR, info->rbufs[0].pdesc);
|
||||
|
||||
if (info->params.mode != MGSL_MODE_ASYNC) {
|
||||
/* enable rx DMA and DMA interrupt */
|
||||
wr_reg32(info, RDCSR, (BIT2 + BIT0));
|
||||
if (info->rx_pio) {
|
||||
/* rx request when rx FIFO not empty */
|
||||
wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14));
|
||||
slgt_irq_on(info, IRQ_RXDATA);
|
||||
if (info->params.mode == MGSL_MODE_ASYNC) {
|
||||
/* enable saving of rx status */
|
||||
wr_reg32(info, RDCSR, BIT6);
|
||||
}
|
||||
} else {
|
||||
/* enable saving of rx status, rx DMA and DMA interrupt */
|
||||
wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0));
|
||||
/* rx request when rx FIFO half full */
|
||||
wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14));
|
||||
/* set 1st descriptor address */
|
||||
wr_reg32(info, RDDAR, info->rbufs[0].pdesc);
|
||||
|
||||
if (info->params.mode != MGSL_MODE_ASYNC) {
|
||||
/* enable rx DMA and DMA interrupt */
|
||||
wr_reg32(info, RDCSR, (BIT2 + BIT0));
|
||||
} else {
|
||||
/* enable saving of rx status, rx DMA and DMA interrupt */
|
||||
wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0));
|
||||
}
|
||||
}
|
||||
|
||||
slgt_irq_on(info, IRQ_RXOVER);
|
||||
@ -4467,6 +4527,8 @@ static void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last
|
||||
static void reset_rbufs(struct slgt_info *info)
|
||||
{
|
||||
free_rbufs(info, 0, info->rbuf_count - 1);
|
||||
info->rbuf_fill_index = 0;
|
||||
info->rbuf_fill_count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3277,13 +3277,16 @@ static int carrier_raised(struct tty_port *port)
|
||||
return (info->serial_signals & SerialSignal_DCD) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void raise_dtr_rts(struct tty_port *port)
|
||||
static void dtr_rts(struct tty_port *port, int on)
|
||||
{
|
||||
SLMP_INFO *info = container_of(port, SLMP_INFO, port);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&info->lock,flags);
|
||||
info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
|
||||
if (on)
|
||||
info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
|
||||
else
|
||||
info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
|
||||
set_signals(info);
|
||||
spin_unlock_irqrestore(&info->lock,flags);
|
||||
}
|
||||
@ -3746,7 +3749,7 @@ static void add_device(SLMP_INFO *info)
|
||||
|
||||
static const struct tty_port_operations port_ops = {
|
||||
.carrier_raised = carrier_raised,
|
||||
.raise_dtr_rts = raise_dtr_rts,
|
||||
.dtr_rts = dtr_rts,
|
||||
};
|
||||
|
||||
/* Allocate and initialize a device instance structure
|
||||
|
@ -29,10 +29,7 @@ static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor,
|
||||
buf = kmalloc(sizeof(*buf), GFP_KERNEL);
|
||||
if (!buf)
|
||||
goto err;
|
||||
if (PAGE_SIZE != N_TTY_BUF_SIZE)
|
||||
buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
|
||||
else
|
||||
buf->data = (unsigned char *)__get_free_page(GFP_KERNEL);
|
||||
buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
|
||||
if (!buf->data)
|
||||
goto err_buf;
|
||||
atomic_set(&buf->count, 1);
|
||||
@ -52,10 +49,7 @@ err:
|
||||
static void tty_audit_buf_free(struct tty_audit_buf *buf)
|
||||
{
|
||||
WARN_ON(buf->valid != 0);
|
||||
if (PAGE_SIZE != N_TTY_BUF_SIZE)
|
||||
kfree(buf->data);
|
||||
else
|
||||
free_page((unsigned long)buf->data);
|
||||
kfree(buf->data);
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
|
@ -295,7 +295,7 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
|
||||
struct tty_driver *p, *res = NULL;
|
||||
int tty_line = 0;
|
||||
int len;
|
||||
char *str;
|
||||
char *str, *stp;
|
||||
|
||||
for (str = name; *str; str++)
|
||||
if ((*str >= '0' && *str <= '9') || *str == ',')
|
||||
@ -311,13 +311,14 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
|
||||
list_for_each_entry(p, &tty_drivers, tty_drivers) {
|
||||
if (strncmp(name, p->name, len) != 0)
|
||||
continue;
|
||||
if (*str == ',')
|
||||
str++;
|
||||
if (*str == '\0')
|
||||
str = NULL;
|
||||
stp = str;
|
||||
if (*stp == ',')
|
||||
stp++;
|
||||
if (*stp == '\0')
|
||||
stp = NULL;
|
||||
|
||||
if (tty_line >= 0 && tty_line <= p->num && p->ops &&
|
||||
p->ops->poll_init && !p->ops->poll_init(p, tty_line, str)) {
|
||||
p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {
|
||||
res = tty_driver_kref_get(p);
|
||||
*line = tty_line;
|
||||
break;
|
||||
@ -469,43 +470,6 @@ void tty_wakeup(struct tty_struct *tty)
|
||||
|
||||
EXPORT_SYMBOL_GPL(tty_wakeup);
|
||||
|
||||
/**
|
||||
* tty_ldisc_flush - flush line discipline queue
|
||||
* @tty: tty
|
||||
*
|
||||
* Flush the line discipline queue (if any) for this tty. If there
|
||||
* is no line discipline active this is a no-op.
|
||||
*/
|
||||
|
||||
void tty_ldisc_flush(struct tty_struct *tty)
|
||||
{
|
||||
struct tty_ldisc *ld = tty_ldisc_ref(tty);
|
||||
if (ld) {
|
||||
if (ld->ops->flush_buffer)
|
||||
ld->ops->flush_buffer(tty);
|
||||
tty_ldisc_deref(ld);
|
||||
}
|
||||
tty_buffer_flush(tty);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tty_ldisc_flush);
|
||||
|
||||
/**
|
||||
* tty_reset_termios - reset terminal state
|
||||
* @tty: tty to reset
|
||||
*
|
||||
* Restore a terminal to the driver default state
|
||||
*/
|
||||
|
||||
static void tty_reset_termios(struct tty_struct *tty)
|
||||
{
|
||||
mutex_lock(&tty->termios_mutex);
|
||||
*tty->termios = tty->driver->init_termios;
|
||||
tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
|
||||
tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
|
||||
mutex_unlock(&tty->termios_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* do_tty_hangup - actual handler for hangup events
|
||||
* @work: tty device
|
||||
@ -535,7 +499,6 @@ static void do_tty_hangup(struct work_struct *work)
|
||||
struct file *cons_filp = NULL;
|
||||
struct file *filp, *f = NULL;
|
||||
struct task_struct *p;
|
||||
struct tty_ldisc *ld;
|
||||
int closecount = 0, n;
|
||||
unsigned long flags;
|
||||
int refs = 0;
|
||||
@ -566,40 +529,8 @@ static void do_tty_hangup(struct work_struct *work)
|
||||
filp->f_op = &hung_up_tty_fops;
|
||||
}
|
||||
file_list_unlock();
|
||||
/*
|
||||
* FIXME! What are the locking issues here? This may me overdoing
|
||||
* things... This question is especially important now that we've
|
||||
* removed the irqlock.
|
||||
*/
|
||||
ld = tty_ldisc_ref(tty);
|
||||
if (ld != NULL) {
|
||||
/* We may have no line discipline at this point */
|
||||
if (ld->ops->flush_buffer)
|
||||
ld->ops->flush_buffer(tty);
|
||||
tty_driver_flush_buffer(tty);
|
||||
if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
|
||||
ld->ops->write_wakeup)
|
||||
ld->ops->write_wakeup(tty);
|
||||
if (ld->ops->hangup)
|
||||
ld->ops->hangup(tty);
|
||||
}
|
||||
/*
|
||||
* FIXME: Once we trust the LDISC code better we can wait here for
|
||||
* ldisc completion and fix the driver call race
|
||||
*/
|
||||
wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
|
||||
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
|
||||
/*
|
||||
* Shutdown the current line discipline, and reset it to
|
||||
* N_TTY.
|
||||
*/
|
||||
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
|
||||
tty_reset_termios(tty);
|
||||
/* Defer ldisc switch */
|
||||
/* tty_deferred_ldisc_switch(N_TTY);
|
||||
|
||||
This should get done automatically when the port closes and
|
||||
tty_release is called */
|
||||
tty_ldisc_hangup(tty);
|
||||
|
||||
read_lock(&tasklist_lock);
|
||||
if (tty->session) {
|
||||
@ -628,12 +559,15 @@ static void do_tty_hangup(struct work_struct *work)
|
||||
read_unlock(&tasklist_lock);
|
||||
|
||||
spin_lock_irqsave(&tty->ctrl_lock, flags);
|
||||
tty->flags = 0;
|
||||
clear_bit(TTY_THROTTLED, &tty->flags);
|
||||
clear_bit(TTY_PUSH, &tty->flags);
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
put_pid(tty->session);
|
||||
put_pid(tty->pgrp);
|
||||
tty->session = NULL;
|
||||
tty->pgrp = NULL;
|
||||
tty->ctrl_status = 0;
|
||||
set_bit(TTY_HUPPED, &tty->flags);
|
||||
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
|
||||
|
||||
/* Account for the p->signal references we killed */
|
||||
@ -659,10 +593,7 @@ static void do_tty_hangup(struct work_struct *work)
|
||||
* can't yet guarantee all that.
|
||||
*/
|
||||
set_bit(TTY_HUPPED, &tty->flags);
|
||||
if (ld) {
|
||||
tty_ldisc_enable(tty);
|
||||
tty_ldisc_deref(ld);
|
||||
}
|
||||
tty_ldisc_enable(tty);
|
||||
unlock_kernel();
|
||||
if (f)
|
||||
fput(f);
|
||||
@ -2480,6 +2411,24 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
|
||||
return tty->ops->tiocmset(tty, file, set, clear);
|
||||
}
|
||||
|
||||
struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
|
||||
{
|
||||
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
|
||||
tty->driver->subtype == PTY_TYPE_MASTER)
|
||||
tty = tty->link;
|
||||
return tty;
|
||||
}
|
||||
EXPORT_SYMBOL(tty_pair_get_tty);
|
||||
|
||||
struct tty_struct *tty_pair_get_pty(struct tty_struct *tty)
|
||||
{
|
||||
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
|
||||
tty->driver->subtype == PTY_TYPE_MASTER)
|
||||
return tty;
|
||||
return tty->link;
|
||||
}
|
||||
EXPORT_SYMBOL(tty_pair_get_pty);
|
||||
|
||||
/*
|
||||
* Split this up, as gcc can choke on it otherwise..
|
||||
*/
|
||||
@ -2495,11 +2444,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
if (tty_paranoia_check(tty, inode, "tty_ioctl"))
|
||||
return -EINVAL;
|
||||
|
||||
real_tty = tty;
|
||||
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
|
||||
tty->driver->subtype == PTY_TYPE_MASTER)
|
||||
real_tty = tty->link;
|
||||
|
||||
real_tty = tty_pair_get_tty(tty);
|
||||
|
||||
/*
|
||||
* Factor out some common prep work
|
||||
@ -2555,7 +2500,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
case TIOCGSID:
|
||||
return tiocgsid(tty, real_tty, p);
|
||||
case TIOCGETD:
|
||||
return put_user(tty->ldisc.ops->num, (int __user *)p);
|
||||
return put_user(tty->ldisc->ops->num, (int __user *)p);
|
||||
case TIOCSETD:
|
||||
return tiocsetd(tty, p);
|
||||
/*
|
||||
@ -2770,6 +2715,7 @@ void initialize_tty_struct(struct tty_struct *tty,
|
||||
tty->buf.head = tty->buf.tail = NULL;
|
||||
tty_buffer_init(tty);
|
||||
mutex_init(&tty->termios_mutex);
|
||||
mutex_init(&tty->ldisc_mutex);
|
||||
init_waitqueue_head(&tty->write_wait);
|
||||
init_waitqueue_head(&tty->read_wait);
|
||||
INIT_WORK(&tty->hangup_work, do_tty_hangup);
|
||||
|
@ -97,14 +97,19 @@ EXPORT_SYMBOL(tty_driver_flush_buffer);
|
||||
* @tty: terminal
|
||||
*
|
||||
* Indicate that a tty should stop transmitting data down the stack.
|
||||
* Takes the termios mutex to protect against parallel throttle/unthrottle
|
||||
* and also to ensure the driver can consistently reference its own
|
||||
* termios data at this point when implementing software flow control.
|
||||
*/
|
||||
|
||||
void tty_throttle(struct tty_struct *tty)
|
||||
{
|
||||
mutex_lock(&tty->termios_mutex);
|
||||
/* check TTY_THROTTLED first so it indicates our state */
|
||||
if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) &&
|
||||
tty->ops->throttle)
|
||||
tty->ops->throttle(tty);
|
||||
mutex_unlock(&tty->termios_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(tty_throttle);
|
||||
|
||||
@ -113,13 +118,21 @@ EXPORT_SYMBOL(tty_throttle);
|
||||
* @tty: terminal
|
||||
*
|
||||
* Indicate that a tty may continue transmitting data down the stack.
|
||||
* Takes the termios mutex to protect against parallel throttle/unthrottle
|
||||
* and also to ensure the driver can consistently reference its own
|
||||
* termios data at this point when implementing software flow control.
|
||||
*
|
||||
* Drivers should however remember that the stack can issue a throttle,
|
||||
* then change flow control method, then unthrottle.
|
||||
*/
|
||||
|
||||
void tty_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
mutex_lock(&tty->termios_mutex);
|
||||
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
|
||||
tty->ops->unthrottle)
|
||||
tty->ops->unthrottle(tty);
|
||||
mutex_unlock(&tty->termios_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(tty_unthrottle);
|
||||
|
||||
@ -613,9 +626,25 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void copy_termios(struct tty_struct *tty, struct ktermios *kterm)
|
||||
{
|
||||
mutex_lock(&tty->termios_mutex);
|
||||
memcpy(kterm, tty->termios, sizeof(struct ktermios));
|
||||
mutex_unlock(&tty->termios_mutex);
|
||||
}
|
||||
|
||||
static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm)
|
||||
{
|
||||
mutex_lock(&tty->termios_mutex);
|
||||
memcpy(kterm, tty->termios_locked, sizeof(struct ktermios));
|
||||
mutex_unlock(&tty->termios_mutex);
|
||||
}
|
||||
|
||||
static int get_termio(struct tty_struct *tty, struct termio __user *termio)
|
||||
{
|
||||
if (kernel_termios_to_user_termio(termio, tty->termios))
|
||||
struct ktermios kterm;
|
||||
copy_termios(tty, &kterm);
|
||||
if (kernel_termios_to_user_termio(termio, &kterm))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
@ -917,6 +946,8 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
|
||||
struct tty_struct *real_tty;
|
||||
void __user *p = (void __user *)arg;
|
||||
int ret = 0;
|
||||
struct ktermios kterm;
|
||||
struct termiox ktermx;
|
||||
|
||||
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
|
||||
tty->driver->subtype == PTY_TYPE_MASTER)
|
||||
@ -952,23 +983,20 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
|
||||
return set_termios(real_tty, p, TERMIOS_OLD);
|
||||
#ifndef TCGETS2
|
||||
case TCGETS:
|
||||
mutex_lock(&real_tty->termios_mutex);
|
||||
if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios))
|
||||
copy_termios(real_tty, &kterm);
|
||||
if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
|
||||
ret = -EFAULT;
|
||||
mutex_unlock(&real_tty->termios_mutex);
|
||||
return ret;
|
||||
#else
|
||||
case TCGETS:
|
||||
mutex_lock(&real_tty->termios_mutex);
|
||||
if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios))
|
||||
copy_termios(real_tty, &kterm);
|
||||
if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
|
||||
ret = -EFAULT;
|
||||
mutex_unlock(&real_tty->termios_mutex);
|
||||
return ret;
|
||||
case TCGETS2:
|
||||
mutex_lock(&real_tty->termios_mutex);
|
||||
if (kernel_termios_to_user_termios((struct termios2 __user *)arg, real_tty->termios))
|
||||
copy_termios(real_tty, &kterm);
|
||||
if (kernel_termios_to_user_termios((struct termios2 __user *)arg, &kterm))
|
||||
ret = -EFAULT;
|
||||
mutex_unlock(&real_tty->termios_mutex);
|
||||
return ret;
|
||||
case TCSETSF2:
|
||||
return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT);
|
||||
@ -987,34 +1015,36 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
|
||||
return set_termios(real_tty, p, TERMIOS_TERMIO);
|
||||
#ifndef TCGETS2
|
||||
case TIOCGLCKTRMIOS:
|
||||
mutex_lock(&real_tty->termios_mutex);
|
||||
if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios_locked))
|
||||
copy_termios_locked(real_tty, &kterm);
|
||||
if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
|
||||
ret = -EFAULT;
|
||||
mutex_unlock(&real_tty->termios_mutex);
|
||||
return ret;
|
||||
case TIOCSLCKTRMIOS:
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
mutex_lock(&real_tty->termios_mutex);
|
||||
if (user_termios_to_kernel_termios(real_tty->termios_locked,
|
||||
copy_termios_locked(real_tty, &kterm);
|
||||
if (user_termios_to_kernel_termios(&kterm,
|
||||
(struct termios __user *) arg))
|
||||
ret = -EFAULT;
|
||||
return -EFAULT;
|
||||
mutex_lock(&real_tty->termios_mutex);
|
||||
memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios));
|
||||
mutex_unlock(&real_tty->termios_mutex);
|
||||
return ret;
|
||||
return 0;
|
||||
#else
|
||||
case TIOCGLCKTRMIOS:
|
||||
mutex_lock(&real_tty->termios_mutex);
|
||||
if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios_locked))
|
||||
copy_termios_locked(real_tty, &kterm);
|
||||
if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
|
||||
ret = -EFAULT;
|
||||
mutex_unlock(&real_tty->termios_mutex);
|
||||
return ret;
|
||||
case TIOCSLCKTRMIOS:
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
ret = -EPERM;
|
||||
mutex_lock(&real_tty->termios_mutex);
|
||||
if (user_termios_to_kernel_termios_1(real_tty->termios_locked,
|
||||
return -EPERM;
|
||||
copy_termios_locked(real_tty, &kterm);
|
||||
if (user_termios_to_kernel_termios_1(&kterm,
|
||||
(struct termios __user *) arg))
|
||||
ret = -EFAULT;
|
||||
return -EFAULT;
|
||||
mutex_lock(&real_tty->termios_mutex);
|
||||
memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios));
|
||||
mutex_unlock(&real_tty->termios_mutex);
|
||||
return ret;
|
||||
#endif
|
||||
@ -1023,9 +1053,10 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
|
||||
if (real_tty->termiox == NULL)
|
||||
return -EINVAL;
|
||||
mutex_lock(&real_tty->termios_mutex);
|
||||
if (copy_to_user(p, real_tty->termiox, sizeof(struct termiox)))
|
||||
ret = -EFAULT;
|
||||
memcpy(&ktermx, real_tty->termiox, sizeof(struct termiox));
|
||||
mutex_unlock(&real_tty->termios_mutex);
|
||||
if (copy_to_user(p, &ktermx, sizeof(struct termiox)))
|
||||
ret = -EFAULT;
|
||||
return ret;
|
||||
case TCSETX:
|
||||
return set_termiox(real_tty, p, 0);
|
||||
@ -1035,10 +1066,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
|
||||
return set_termiox(real_tty, p, TERMIOS_FLUSH);
|
||||
#endif
|
||||
case TIOCGSOFTCAR:
|
||||
mutex_lock(&real_tty->termios_mutex);
|
||||
ret = put_user(C_CLOCAL(real_tty) ? 1 : 0,
|
||||
copy_termios(real_tty, &kterm);
|
||||
ret = put_user((kterm.c_cflag & CLOCAL) ? 1 : 0,
|
||||
(int __user *)arg);
|
||||
mutex_unlock(&real_tty->termios_mutex);
|
||||
return ret;
|
||||
case TIOCSSOFTCAR:
|
||||
if (get_user(arg, (unsigned int __user *) arg))
|
||||
|
@ -115,19 +115,22 @@ EXPORT_SYMBOL(tty_unregister_ldisc);
|
||||
/**
|
||||
* tty_ldisc_try_get - try and reference an ldisc
|
||||
* @disc: ldisc number
|
||||
* @ld: tty ldisc structure to complete
|
||||
*
|
||||
* Attempt to open and lock a line discipline into place. Return
|
||||
* the line discipline refcounted and assigned in ld. On an error
|
||||
* report the error code back
|
||||
* the line discipline refcounted or an error.
|
||||
*/
|
||||
|
||||
static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld)
|
||||
static struct tty_ldisc *tty_ldisc_try_get(int disc)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct tty_ldisc *ld;
|
||||
struct tty_ldisc_ops *ldops;
|
||||
int err = -EINVAL;
|
||||
|
||||
|
||||
ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);
|
||||
if (ld == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
spin_lock_irqsave(&tty_ldisc_lock, flags);
|
||||
ld->ops = NULL;
|
||||
ldops = tty_ldiscs[disc];
|
||||
@ -140,17 +143,19 @@ static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld)
|
||||
/* lock it */
|
||||
ldops->refcount++;
|
||||
ld->ops = ldops;
|
||||
ld->refcount = 0;
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
||||
return err;
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return ld;
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_get - take a reference to an ldisc
|
||||
* @disc: ldisc number
|
||||
* @ld: tty line discipline structure to use
|
||||
*
|
||||
* Takes a reference to a line discipline. Deals with refcounts and
|
||||
* module locking counts. Returns NULL if the discipline is not available.
|
||||
@ -161,52 +166,54 @@ static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld)
|
||||
* takes tty_ldisc_lock to guard against ldisc races
|
||||
*/
|
||||
|
||||
static int tty_ldisc_get(int disc, struct tty_ldisc *ld)
|
||||
static struct tty_ldisc *tty_ldisc_get(int disc)
|
||||
{
|
||||
int err;
|
||||
struct tty_ldisc *ld;
|
||||
|
||||
if (disc < N_TTY || disc >= NR_LDISCS)
|
||||
return -EINVAL;
|
||||
err = tty_ldisc_try_get(disc, ld);
|
||||
if (err < 0) {
|
||||
return ERR_PTR(-EINVAL);
|
||||
ld = tty_ldisc_try_get(disc);
|
||||
if (IS_ERR(ld)) {
|
||||
request_module("tty-ldisc-%d", disc);
|
||||
err = tty_ldisc_try_get(disc, ld);
|
||||
ld = tty_ldisc_try_get(disc);
|
||||
}
|
||||
return err;
|
||||
return ld;
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_put - drop ldisc reference
|
||||
* @disc: ldisc number
|
||||
* @ld: ldisc
|
||||
*
|
||||
* Drop a reference to a line discipline. Manage refcounts and
|
||||
* module usage counts
|
||||
* module usage counts. Free the ldisc once the recount hits zero.
|
||||
*
|
||||
* Locking:
|
||||
* takes tty_ldisc_lock to guard against ldisc races
|
||||
*/
|
||||
|
||||
static void tty_ldisc_put(struct tty_ldisc_ops *ld)
|
||||
static void tty_ldisc_put(struct tty_ldisc *ld)
|
||||
{
|
||||
unsigned long flags;
|
||||
int disc = ld->num;
|
||||
int disc = ld->ops->num;
|
||||
struct tty_ldisc_ops *ldo;
|
||||
|
||||
BUG_ON(disc < N_TTY || disc >= NR_LDISCS);
|
||||
|
||||
spin_lock_irqsave(&tty_ldisc_lock, flags);
|
||||
ld = tty_ldiscs[disc];
|
||||
BUG_ON(ld->refcount == 0);
|
||||
ld->refcount--;
|
||||
module_put(ld->owner);
|
||||
ldo = tty_ldiscs[disc];
|
||||
BUG_ON(ldo->refcount == 0);
|
||||
ldo->refcount--;
|
||||
module_put(ldo->owner);
|
||||
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
||||
kfree(ld);
|
||||
}
|
||||
|
||||
static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos)
|
||||
static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
return (*pos < NR_LDISCS) ? pos : NULL;
|
||||
}
|
||||
|
||||
static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
static void *tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
(*pos)++;
|
||||
return (*pos < NR_LDISCS) ? pos : NULL;
|
||||
@ -219,12 +226,13 @@ static void tty_ldiscs_seq_stop(struct seq_file *m, void *v)
|
||||
static int tty_ldiscs_seq_show(struct seq_file *m, void *v)
|
||||
{
|
||||
int i = *(loff_t *)v;
|
||||
struct tty_ldisc ld;
|
||||
|
||||
if (tty_ldisc_get(i, &ld) < 0)
|
||||
struct tty_ldisc *ld;
|
||||
|
||||
ld = tty_ldisc_try_get(i);
|
||||
if (IS_ERR(ld))
|
||||
return 0;
|
||||
seq_printf(m, "%-10s %2d\n", ld.ops->name ? ld.ops->name : "???", i);
|
||||
tty_ldisc_put(ld.ops);
|
||||
seq_printf(m, "%-10s %2d\n", ld->ops->name ? ld->ops->name : "???", i);
|
||||
tty_ldisc_put(ld);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -263,8 +271,7 @@ const struct file_operations tty_ldiscs_proc_fops = {
|
||||
|
||||
static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
|
||||
{
|
||||
ld->refcount = 0;
|
||||
tty->ldisc = *ld;
|
||||
tty->ldisc = ld;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -286,7 +293,7 @@ static int tty_ldisc_try(struct tty_struct *tty)
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&tty_ldisc_lock, flags);
|
||||
ld = &tty->ldisc;
|
||||
ld = tty->ldisc;
|
||||
if (test_bit(TTY_LDISC, &tty->flags)) {
|
||||
ld->refcount++;
|
||||
ret = 1;
|
||||
@ -315,10 +322,9 @@ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
|
||||
{
|
||||
/* wait_event is a macro */
|
||||
wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
|
||||
WARN_ON(tty->ldisc.refcount == 0);
|
||||
return &tty->ldisc;
|
||||
WARN_ON(tty->ldisc->refcount == 0);
|
||||
return tty->ldisc;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
|
||||
|
||||
/**
|
||||
@ -335,10 +341,9 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
|
||||
struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
|
||||
{
|
||||
if (tty_ldisc_try(tty))
|
||||
return &tty->ldisc;
|
||||
return tty->ldisc;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tty_ldisc_ref);
|
||||
|
||||
/**
|
||||
@ -366,7 +371,6 @@ void tty_ldisc_deref(struct tty_ldisc *ld)
|
||||
wake_up(&tty_ldisc_wait);
|
||||
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tty_ldisc_deref);
|
||||
|
||||
/**
|
||||
@ -388,6 +392,26 @@ void tty_ldisc_enable(struct tty_struct *tty)
|
||||
wake_up(&tty_ldisc_wait);
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_flush - flush line discipline queue
|
||||
* @tty: tty
|
||||
*
|
||||
* Flush the line discipline queue (if any) for this tty. If there
|
||||
* is no line discipline active this is a no-op.
|
||||
*/
|
||||
|
||||
void tty_ldisc_flush(struct tty_struct *tty)
|
||||
{
|
||||
struct tty_ldisc *ld = tty_ldisc_ref(tty);
|
||||
if (ld) {
|
||||
if (ld->ops->flush_buffer)
|
||||
ld->ops->flush_buffer(tty);
|
||||
tty_ldisc_deref(ld);
|
||||
}
|
||||
tty_buffer_flush(tty);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tty_ldisc_flush);
|
||||
|
||||
/**
|
||||
* tty_set_termios_ldisc - set ldisc field
|
||||
* @tty: tty structure
|
||||
@ -407,6 +431,39 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
|
||||
mutex_unlock(&tty->termios_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_open - open a line discipline
|
||||
* @tty: tty we are opening the ldisc on
|
||||
* @ld: discipline to open
|
||||
*
|
||||
* A helper opening method. Also a convenient debugging and check
|
||||
* point.
|
||||
*/
|
||||
|
||||
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
|
||||
{
|
||||
WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
|
||||
if (ld->ops->open)
|
||||
return ld->ops->open(tty);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_close - close a line discipline
|
||||
* @tty: tty we are opening the ldisc on
|
||||
* @ld: discipline to close
|
||||
*
|
||||
* A helper close method. Also a convenient debugging and check
|
||||
* point.
|
||||
*/
|
||||
|
||||
static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
|
||||
{
|
||||
WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags));
|
||||
clear_bit(TTY_LDISC_OPEN, &tty->flags);
|
||||
if (ld->ops->close)
|
||||
ld->ops->close(tty);
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_restore - helper for tty ldisc change
|
||||
@ -420,66 +477,136 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
|
||||
static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
|
||||
{
|
||||
char buf[64];
|
||||
struct tty_ldisc new_ldisc;
|
||||
struct tty_ldisc *new_ldisc;
|
||||
int r;
|
||||
|
||||
/* There is an outstanding reference here so this is safe */
|
||||
tty_ldisc_get(old->ops->num, old);
|
||||
old = tty_ldisc_get(old->ops->num);
|
||||
WARN_ON(IS_ERR(old));
|
||||
tty_ldisc_assign(tty, old);
|
||||
tty_set_termios_ldisc(tty, old->ops->num);
|
||||
if (old->ops->open && (old->ops->open(tty) < 0)) {
|
||||
tty_ldisc_put(old->ops);
|
||||
if (tty_ldisc_open(tty, old) < 0) {
|
||||
tty_ldisc_put(old);
|
||||
/* This driver is always present */
|
||||
if (tty_ldisc_get(N_TTY, &new_ldisc) < 0)
|
||||
new_ldisc = tty_ldisc_get(N_TTY);
|
||||
if (IS_ERR(new_ldisc))
|
||||
panic("n_tty: get");
|
||||
tty_ldisc_assign(tty, &new_ldisc);
|
||||
tty_ldisc_assign(tty, new_ldisc);
|
||||
tty_set_termios_ldisc(tty, N_TTY);
|
||||
if (new_ldisc.ops->open) {
|
||||
int r = new_ldisc.ops->open(tty);
|
||||
if (r < 0)
|
||||
panic("Couldn't open N_TTY ldisc for "
|
||||
"%s --- error %d.",
|
||||
tty_name(tty, buf), r);
|
||||
}
|
||||
r = tty_ldisc_open(tty, new_ldisc);
|
||||
if (r < 0)
|
||||
panic("Couldn't open N_TTY ldisc for "
|
||||
"%s --- error %d.",
|
||||
tty_name(tty, buf), r);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_halt - shut down the line discipline
|
||||
* @tty: tty device
|
||||
*
|
||||
* Shut down the line discipline and work queue for this tty device.
|
||||
* The TTY_LDISC flag being cleared ensures no further references can
|
||||
* be obtained while the delayed work queue halt ensures that no more
|
||||
* data is fed to the ldisc.
|
||||
*
|
||||
* In order to wait for any existing references to complete see
|
||||
* tty_ldisc_wait_idle.
|
||||
*/
|
||||
|
||||
static int tty_ldisc_halt(struct tty_struct *tty)
|
||||
{
|
||||
clear_bit(TTY_LDISC, &tty->flags);
|
||||
return cancel_delayed_work(&tty->buf.work);
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_wait_idle - wait for the ldisc to become idle
|
||||
* @tty: tty to wait for
|
||||
*
|
||||
* Wait for the line discipline to become idle. The discipline must
|
||||
* have been halted for this to guarantee it remains idle.
|
||||
*
|
||||
* tty_ldisc_lock protects the ref counts currently.
|
||||
*/
|
||||
|
||||
static int tty_ldisc_wait_idle(struct tty_struct *tty)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&tty_ldisc_lock, flags);
|
||||
while (tty->ldisc->refcount) {
|
||||
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
||||
if (wait_event_timeout(tty_ldisc_wait,
|
||||
tty->ldisc->refcount == 0, 5 * HZ) == 0)
|
||||
return -EBUSY;
|
||||
spin_lock_irqsave(&tty_ldisc_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_set_ldisc - set line discipline
|
||||
* @tty: the terminal to set
|
||||
* @ldisc: the line discipline
|
||||
*
|
||||
* Set the discipline of a tty line. Must be called from a process
|
||||
* context.
|
||||
* context. The ldisc change logic has to protect itself against any
|
||||
* overlapping ldisc change (including on the other end of pty pairs),
|
||||
* the close of one side of a tty/pty pair, and eventually hangup.
|
||||
*
|
||||
* Locking: takes tty_ldisc_lock.
|
||||
* called functions take termios_mutex
|
||||
* Locking: takes tty_ldisc_lock, termios_mutex
|
||||
*/
|
||||
|
||||
int tty_set_ldisc(struct tty_struct *tty, int ldisc)
|
||||
{
|
||||
int retval;
|
||||
struct tty_ldisc o_ldisc, new_ldisc;
|
||||
int work;
|
||||
unsigned long flags;
|
||||
struct tty_ldisc *o_ldisc, *new_ldisc;
|
||||
int work, o_work = 0;
|
||||
struct tty_struct *o_tty;
|
||||
|
||||
restart:
|
||||
/* This is a bit ugly for now but means we can break the 'ldisc
|
||||
is part of the tty struct' assumption later */
|
||||
retval = tty_ldisc_get(ldisc, &new_ldisc);
|
||||
if (retval)
|
||||
return retval;
|
||||
new_ldisc = tty_ldisc_get(ldisc);
|
||||
if (IS_ERR(new_ldisc))
|
||||
return PTR_ERR(new_ldisc);
|
||||
|
||||
/*
|
||||
* We need to look at the tty locking here for pty/tty pairs
|
||||
* when both sides try to change in parallel.
|
||||
*/
|
||||
|
||||
o_tty = tty->link; /* o_tty is the pty side or NULL */
|
||||
|
||||
|
||||
/*
|
||||
* Check the no-op case
|
||||
*/
|
||||
|
||||
if (tty->ldisc->ops->num == ldisc) {
|
||||
tty_ldisc_put(new_ldisc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Problem: What do we do if this blocks ?
|
||||
* We could deadlock here
|
||||
*/
|
||||
|
||||
tty_wait_until_sent(tty, 0);
|
||||
|
||||
if (tty->ldisc.ops->num == ldisc) {
|
||||
tty_ldisc_put(new_ldisc.ops);
|
||||
return 0;
|
||||
mutex_lock(&tty->ldisc_mutex);
|
||||
|
||||
/*
|
||||
* We could be midstream of another ldisc change which has
|
||||
* dropped the lock during processing. If so we need to wait.
|
||||
*/
|
||||
|
||||
while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
|
||||
mutex_unlock(&tty->ldisc_mutex);
|
||||
wait_event(tty_ldisc_wait,
|
||||
test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
|
||||
mutex_lock(&tty->ldisc_mutex);
|
||||
}
|
||||
set_bit(TTY_LDISC_CHANGING, &tty->flags);
|
||||
|
||||
/*
|
||||
* No more input please, we are switching. The new ldisc
|
||||
@ -489,8 +616,6 @@ restart:
|
||||
tty->receive_room = 0;
|
||||
|
||||
o_ldisc = tty->ldisc;
|
||||
o_tty = tty->link;
|
||||
|
||||
/*
|
||||
* Make sure we don't change while someone holds a
|
||||
* reference to the line discipline. The TTY_LDISC bit
|
||||
@ -501,108 +626,181 @@ restart:
|
||||
* with a userspace app continually trying to use the tty in
|
||||
* parallel to the change and re-referencing the tty.
|
||||
*/
|
||||
clear_bit(TTY_LDISC, &tty->flags);
|
||||
if (o_tty)
|
||||
clear_bit(TTY_LDISC, &o_tty->flags);
|
||||
|
||||
spin_lock_irqsave(&tty_ldisc_lock, flags);
|
||||
if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) {
|
||||
if (tty->ldisc.refcount) {
|
||||
/* Free the new ldisc we grabbed. Must drop the lock
|
||||
first. */
|
||||
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
||||
tty_ldisc_put(o_ldisc.ops);
|
||||
/*
|
||||
* There are several reasons we may be busy, including
|
||||
* random momentary I/O traffic. We must therefore
|
||||
* retry. We could distinguish between blocking ops
|
||||
* and retries if we made tty_ldisc_wait() smarter.
|
||||
* That is up for discussion.
|
||||
*/
|
||||
if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0)
|
||||
return -ERESTARTSYS;
|
||||
goto restart;
|
||||
}
|
||||
if (o_tty && o_tty->ldisc.refcount) {
|
||||
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
||||
tty_ldisc_put(o_tty->ldisc.ops);
|
||||
if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0)
|
||||
return -ERESTARTSYS;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If the TTY_LDISC bit is set, then we are racing against
|
||||
* another ldisc change
|
||||
*/
|
||||
if (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
|
||||
struct tty_ldisc *ld;
|
||||
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
||||
tty_ldisc_put(new_ldisc.ops);
|
||||
ld = tty_ldisc_ref_wait(tty);
|
||||
tty_ldisc_deref(ld);
|
||||
goto restart;
|
||||
}
|
||||
/*
|
||||
* This flag is used to avoid two parallel ldisc changes. Once
|
||||
* open and close are fine grained locked this may work better
|
||||
* as a mutex shared with the open/close/hup paths
|
||||
*/
|
||||
set_bit(TTY_LDISC_CHANGING, &tty->flags);
|
||||
work = tty_ldisc_halt(tty);
|
||||
if (o_tty)
|
||||
set_bit(TTY_LDISC_CHANGING, &o_tty->flags);
|
||||
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
||||
|
||||
o_work = tty_ldisc_halt(o_tty);
|
||||
|
||||
/*
|
||||
* From this point on we know nobody has an ldisc
|
||||
* usage reference, nor can they obtain one until
|
||||
* we say so later on.
|
||||
* Wait for ->hangup_work and ->buf.work handlers to terminate.
|
||||
* We must drop the mutex here in case a hangup is also in process.
|
||||
*/
|
||||
|
||||
work = cancel_delayed_work(&tty->buf.work);
|
||||
/*
|
||||
* Wait for ->hangup_work and ->buf.work handlers to terminate
|
||||
* MUST NOT hold locks here.
|
||||
*/
|
||||
mutex_unlock(&tty->ldisc_mutex);
|
||||
|
||||
flush_scheduled_work();
|
||||
|
||||
/* Let any existing reference holders finish */
|
||||
retval = tty_ldisc_wait_idle(tty);
|
||||
if (retval < 0) {
|
||||
clear_bit(TTY_LDISC_CHANGING, &tty->flags);
|
||||
tty_ldisc_put(new_ldisc);
|
||||
return retval;
|
||||
}
|
||||
|
||||
mutex_lock(&tty->ldisc_mutex);
|
||||
if (test_bit(TTY_HUPPED, &tty->flags)) {
|
||||
/* We were raced by the hangup method. It will have stomped
|
||||
the ldisc data and closed the ldisc down */
|
||||
clear_bit(TTY_LDISC_CHANGING, &tty->flags);
|
||||
mutex_unlock(&tty->ldisc_mutex);
|
||||
tty_ldisc_put(new_ldisc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Shutdown the current discipline. */
|
||||
if (o_ldisc.ops->close)
|
||||
(o_ldisc.ops->close)(tty);
|
||||
tty_ldisc_close(tty, o_ldisc);
|
||||
|
||||
/* Now set up the new line discipline. */
|
||||
tty_ldisc_assign(tty, &new_ldisc);
|
||||
tty_ldisc_assign(tty, new_ldisc);
|
||||
tty_set_termios_ldisc(tty, ldisc);
|
||||
if (new_ldisc.ops->open)
|
||||
retval = (new_ldisc.ops->open)(tty);
|
||||
|
||||
retval = tty_ldisc_open(tty, new_ldisc);
|
||||
if (retval < 0) {
|
||||
tty_ldisc_put(new_ldisc.ops);
|
||||
tty_ldisc_restore(tty, &o_ldisc);
|
||||
/* Back to the old one or N_TTY if we can't */
|
||||
tty_ldisc_put(new_ldisc);
|
||||
tty_ldisc_restore(tty, o_ldisc);
|
||||
}
|
||||
|
||||
/* At this point we hold a reference to the new ldisc and a
|
||||
a reference to the old ldisc. If we ended up flipping back
|
||||
to the existing ldisc we have two references to it */
|
||||
|
||||
if (tty->ldisc.ops->num != o_ldisc.ops->num && tty->ops->set_ldisc)
|
||||
if (tty->ldisc->ops->num != o_ldisc->ops->num && tty->ops->set_ldisc)
|
||||
tty->ops->set_ldisc(tty);
|
||||
|
||||
tty_ldisc_put(o_ldisc.ops);
|
||||
tty_ldisc_put(o_ldisc);
|
||||
|
||||
/*
|
||||
* Allow ldisc referencing to occur as soon as the driver
|
||||
* ldisc callback completes.
|
||||
* Allow ldisc referencing to occur again
|
||||
*/
|
||||
|
||||
tty_ldisc_enable(tty);
|
||||
if (o_tty)
|
||||
tty_ldisc_enable(o_tty);
|
||||
|
||||
/* Restart it in case no characters kick it off. Safe if
|
||||
/* Restart the work queue in case no characters kick it off. Safe if
|
||||
already running */
|
||||
if (work)
|
||||
schedule_delayed_work(&tty->buf.work, 1);
|
||||
if (o_work)
|
||||
schedule_delayed_work(&o_tty->buf.work, 1);
|
||||
mutex_unlock(&tty->ldisc_mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_reset_termios - reset terminal state
|
||||
* @tty: tty to reset
|
||||
*
|
||||
* Restore a terminal to the driver default state.
|
||||
*/
|
||||
|
||||
static void tty_reset_termios(struct tty_struct *tty)
|
||||
{
|
||||
mutex_lock(&tty->termios_mutex);
|
||||
*tty->termios = tty->driver->init_termios;
|
||||
tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
|
||||
tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
|
||||
mutex_unlock(&tty->termios_mutex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tty_ldisc_reinit - reinitialise the tty ldisc
|
||||
* @tty: tty to reinit
|
||||
*
|
||||
* Switch the tty back to N_TTY line discipline and leave the
|
||||
* ldisc state closed
|
||||
*/
|
||||
|
||||
static void tty_ldisc_reinit(struct tty_struct *tty)
|
||||
{
|
||||
struct tty_ldisc *ld;
|
||||
|
||||
tty_ldisc_close(tty, tty->ldisc);
|
||||
tty_ldisc_put(tty->ldisc);
|
||||
tty->ldisc = NULL;
|
||||
/*
|
||||
* Switch the line discipline back
|
||||
*/
|
||||
ld = tty_ldisc_get(N_TTY);
|
||||
BUG_ON(IS_ERR(ld));
|
||||
tty_ldisc_assign(tty, ld);
|
||||
tty_set_termios_ldisc(tty, N_TTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_hangup - hangup ldisc reset
|
||||
* @tty: tty being hung up
|
||||
*
|
||||
* Some tty devices reset their termios when they receive a hangup
|
||||
* event. In that situation we must also switch back to N_TTY properly
|
||||
* before we reset the termios data.
|
||||
*
|
||||
* Locking: We can take the ldisc mutex as the rest of the code is
|
||||
* careful to allow for this.
|
||||
*
|
||||
* In the pty pair case this occurs in the close() path of the
|
||||
* tty itself so we must be careful about locking rules.
|
||||
*/
|
||||
|
||||
void tty_ldisc_hangup(struct tty_struct *tty)
|
||||
{
|
||||
struct tty_ldisc *ld;
|
||||
|
||||
/*
|
||||
* FIXME! What are the locking issues here? This may me overdoing
|
||||
* things... This question is especially important now that we've
|
||||
* removed the irqlock.
|
||||
*/
|
||||
ld = tty_ldisc_ref(tty);
|
||||
if (ld != NULL) {
|
||||
/* We may have no line discipline at this point */
|
||||
if (ld->ops->flush_buffer)
|
||||
ld->ops->flush_buffer(tty);
|
||||
tty_driver_flush_buffer(tty);
|
||||
if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
|
||||
ld->ops->write_wakeup)
|
||||
ld->ops->write_wakeup(tty);
|
||||
if (ld->ops->hangup)
|
||||
ld->ops->hangup(tty);
|
||||
tty_ldisc_deref(ld);
|
||||
}
|
||||
/*
|
||||
* FIXME: Once we trust the LDISC code better we can wait here for
|
||||
* ldisc completion and fix the driver call race
|
||||
*/
|
||||
wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
|
||||
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
|
||||
/*
|
||||
* Shutdown the current line discipline, and reset it to
|
||||
* N_TTY.
|
||||
*/
|
||||
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
|
||||
/* Avoid racing set_ldisc */
|
||||
mutex_lock(&tty->ldisc_mutex);
|
||||
/* Switch back to N_TTY */
|
||||
tty_ldisc_reinit(tty);
|
||||
/* At this point we have a closed ldisc and we want to
|
||||
reopen it. We could defer this to the next open but
|
||||
it means auditing a lot of other paths so this is a FIXME */
|
||||
WARN_ON(tty_ldisc_open(tty, tty->ldisc));
|
||||
tty_ldisc_enable(tty);
|
||||
mutex_unlock(&tty->ldisc_mutex);
|
||||
tty_reset_termios(tty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_setup - open line discipline
|
||||
@ -610,24 +808,23 @@ restart:
|
||||
* @o_tty: pair tty for pty/tty pairs
|
||||
*
|
||||
* Called during the initial open of a tty/pty pair in order to set up the
|
||||
* line discplines and bind them to the tty.
|
||||
* line disciplines and bind them to the tty. This has no locking issues
|
||||
* as the device isn't yet active.
|
||||
*/
|
||||
|
||||
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
|
||||
{
|
||||
struct tty_ldisc *ld = &tty->ldisc;
|
||||
struct tty_ldisc *ld = tty->ldisc;
|
||||
int retval;
|
||||
|
||||
if (ld->ops->open) {
|
||||
retval = (ld->ops->open)(tty);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
if (o_tty && o_tty->ldisc.ops->open) {
|
||||
retval = (o_tty->ldisc.ops->open)(o_tty);
|
||||
retval = tty_ldisc_open(tty, ld);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (o_tty) {
|
||||
retval = tty_ldisc_open(o_tty, o_tty->ldisc);
|
||||
if (retval) {
|
||||
if (ld->ops->close)
|
||||
(ld->ops->close)(tty);
|
||||
tty_ldisc_close(tty, ld);
|
||||
return retval;
|
||||
}
|
||||
tty_ldisc_enable(o_tty);
|
||||
@ -635,32 +832,25 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
|
||||
tty_ldisc_enable(tty);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_release - release line discipline
|
||||
* @tty: tty being shut down
|
||||
* @o_tty: pair tty for pty/tty pairs
|
||||
*
|
||||
* Called during the final close of a tty/pty pair in order to shut down the
|
||||
* line discpline layer.
|
||||
* Called during the final close of a tty/pty pair in order to shut down
|
||||
* the line discpline layer. On exit the ldisc assigned is N_TTY and the
|
||||
* ldisc has not been opened.
|
||||
*/
|
||||
|
||||
void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct tty_ldisc ld;
|
||||
/*
|
||||
* Prevent flush_to_ldisc() from rescheduling the work for later. Then
|
||||
* kill any delayed work. As this is the final close it does not
|
||||
* race with the set_ldisc code path.
|
||||
*/
|
||||
clear_bit(TTY_LDISC, &tty->flags);
|
||||
cancel_delayed_work(&tty->buf.work);
|
||||
|
||||
/*
|
||||
* Wait for ->hangup_work and ->buf.work handlers to terminate
|
||||
*/
|
||||
|
||||
tty_ldisc_halt(tty);
|
||||
flush_scheduled_work();
|
||||
|
||||
/*
|
||||
@ -668,38 +858,19 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
|
||||
* side waiters as the file is closing so user count on the file
|
||||
* side is zero.
|
||||
*/
|
||||
spin_lock_irqsave(&tty_ldisc_lock, flags);
|
||||
while (tty->ldisc.refcount) {
|
||||
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
||||
wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
|
||||
spin_lock_irqsave(&tty_ldisc_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
||||
|
||||
tty_ldisc_wait_idle(tty);
|
||||
|
||||
/*
|
||||
* Shutdown the current line discipline, and reset it to N_TTY.
|
||||
*
|
||||
* FIXME: this MUST get fixed for the new reflocking
|
||||
*/
|
||||
if (tty->ldisc.ops->close)
|
||||
(tty->ldisc.ops->close)(tty);
|
||||
tty_ldisc_put(tty->ldisc.ops);
|
||||
|
||||
/*
|
||||
* Switch the line discipline back
|
||||
*/
|
||||
WARN_ON(tty_ldisc_get(N_TTY, &ld));
|
||||
tty_ldisc_assign(tty, &ld);
|
||||
tty_set_termios_ldisc(tty, N_TTY);
|
||||
if (o_tty) {
|
||||
/* FIXME: could o_tty be in setldisc here ? */
|
||||
clear_bit(TTY_LDISC, &o_tty->flags);
|
||||
if (o_tty->ldisc.ops->close)
|
||||
(o_tty->ldisc.ops->close)(o_tty);
|
||||
tty_ldisc_put(o_tty->ldisc.ops);
|
||||
WARN_ON(tty_ldisc_get(N_TTY, &ld));
|
||||
tty_ldisc_assign(o_tty, &ld);
|
||||
tty_set_termios_ldisc(o_tty, N_TTY);
|
||||
}
|
||||
tty_ldisc_reinit(tty);
|
||||
/* This will need doing differently if we need to lock */
|
||||
if (o_tty)
|
||||
tty_ldisc_release(o_tty, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -712,10 +883,10 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
|
||||
|
||||
void tty_ldisc_init(struct tty_struct *tty)
|
||||
{
|
||||
struct tty_ldisc ld;
|
||||
if (tty_ldisc_get(N_TTY, &ld) < 0)
|
||||
struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
|
||||
if (IS_ERR(ld))
|
||||
panic("n_tty: init_tty");
|
||||
tty_ldisc_assign(tty, &ld);
|
||||
tty_ldisc_assign(tty, ld);
|
||||
}
|
||||
|
||||
void tty_ldisc_begin(void)
|
||||
|
@ -137,7 +137,7 @@ int tty_port_carrier_raised(struct tty_port *port)
|
||||
EXPORT_SYMBOL(tty_port_carrier_raised);
|
||||
|
||||
/**
|
||||
* tty_port_raise_dtr_rts - Riase DTR/RTS
|
||||
* tty_port_raise_dtr_rts - Raise DTR/RTS
|
||||
* @port: tty port
|
||||
*
|
||||
* Wrapper for the DTR/RTS raise logic. For the moment this is used
|
||||
@ -147,11 +147,27 @@ EXPORT_SYMBOL(tty_port_carrier_raised);
|
||||
|
||||
void tty_port_raise_dtr_rts(struct tty_port *port)
|
||||
{
|
||||
if (port->ops->raise_dtr_rts)
|
||||
port->ops->raise_dtr_rts(port);
|
||||
if (port->ops->dtr_rts)
|
||||
port->ops->dtr_rts(port, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(tty_port_raise_dtr_rts);
|
||||
|
||||
/**
|
||||
* tty_port_lower_dtr_rts - Lower DTR/RTS
|
||||
* @port: tty port
|
||||
*
|
||||
* Wrapper for the DTR/RTS raise logic. For the moment this is used
|
||||
* to hide some internal details. This will eventually become entirely
|
||||
* internal to the tty port.
|
||||
*/
|
||||
|
||||
void tty_port_lower_dtr_rts(struct tty_port *port)
|
||||
{
|
||||
if (port->ops->dtr_rts)
|
||||
port->ops->dtr_rts(port, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(tty_port_lower_dtr_rts);
|
||||
|
||||
/**
|
||||
* tty_port_block_til_ready - Waiting logic for tty open
|
||||
* @port: the tty port being opened
|
||||
@ -167,7 +183,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts);
|
||||
* - port flags and counts
|
||||
*
|
||||
* The passed tty_port must implement the carrier_raised method if it can
|
||||
* do carrier detect and the raise_dtr_rts method if it supports software
|
||||
* do carrier detect and the dtr_rts method if it supports software
|
||||
* management of these lines. Note that the dtr/rts raise is done each
|
||||
* iteration as a hangup may have previously dropped them while we wait.
|
||||
*/
|
||||
@ -182,7 +198,8 @@ int tty_port_block_til_ready(struct tty_port *port,
|
||||
|
||||
/* block if port is in the process of being closed */
|
||||
if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
|
||||
interruptible_sleep_on(&port->close_wait);
|
||||
wait_event_interruptible(port->close_wait,
|
||||
!(port->flags & ASYNC_CLOSING));
|
||||
if (port->flags & ASYNC_HUP_NOTIFY)
|
||||
return -EAGAIN;
|
||||
else
|
||||
@ -205,7 +222,6 @@ int tty_port_block_til_ready(struct tty_port *port,
|
||||
before the next open may complete */
|
||||
|
||||
retval = 0;
|
||||
add_wait_queue(&port->open_wait, &wait);
|
||||
|
||||
/* The port lock protects the port counts */
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
@ -219,7 +235,7 @@ int tty_port_block_til_ready(struct tty_port *port,
|
||||
if (tty->termios->c_cflag & CBAUD)
|
||||
tty_port_raise_dtr_rts(port);
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE);
|
||||
/* Check for a hangup or uninitialised port. Return accordingly */
|
||||
if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
|
||||
if (port->flags & ASYNC_HUP_NOTIFY)
|
||||
@ -240,8 +256,7 @@ int tty_port_block_til_ready(struct tty_port *port,
|
||||
}
|
||||
schedule();
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&port->open_wait, &wait);
|
||||
finish_wait(&port->open_wait, &wait);
|
||||
|
||||
/* Update counts. A parallel hangup will have set count to zero and
|
||||
we must not mess that up further */
|
||||
@ -292,6 +307,17 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f
|
||||
if (port->flags & ASYNC_INITIALIZED &&
|
||||
port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
|
||||
tty_wait_until_sent(tty, port->closing_wait);
|
||||
if (port->drain_delay) {
|
||||
unsigned int bps = tty_get_baud_rate(tty);
|
||||
long timeout;
|
||||
|
||||
if (bps > 1200)
|
||||
timeout = max_t(long, (HZ * 10 * port->drain_delay) / bps,
|
||||
HZ / 10);
|
||||
else
|
||||
timeout = 2 * HZ;
|
||||
schedule_timeout_interruptible(timeout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(tty_port_close_start);
|
||||
@ -302,6 +328,9 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
|
||||
|
||||
tty_ldisc_flush(tty);
|
||||
|
||||
if (tty->termios->c_cflag & HUPCL)
|
||||
tty_port_lower_dtr_rts(port);
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
tty->closing = 0;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -287,6 +287,13 @@ static const struct serial8250_config uart_config[] = {
|
||||
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
|
||||
.flags = UART_CAP_FIFO,
|
||||
},
|
||||
[PORT_AR7] = {
|
||||
.name = "AR7",
|
||||
.fifo_size = 16,
|
||||
.tx_loadsz = 16,
|
||||
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
|
||||
.flags = UART_CAP_FIFO | UART_CAP_AFE,
|
||||
},
|
||||
};
|
||||
|
||||
#if defined (CONFIG_SERIAL_8250_AU1X00)
|
||||
|
@ -2776,6 +2776,9 @@ static struct pci_device_id serial_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_OXSEMI, 0x950a,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_b0_2_1130000 },
|
||||
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_C950,
|
||||
PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950, 0, 0,
|
||||
pbn_b0_1_921600 },
|
||||
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||
pbn_b0_4_115200 },
|
||||
|
@ -833,6 +833,7 @@ config SERIAL_IMX
|
||||
bool "IMX serial port support"
|
||||
depends on ARM && (ARCH_IMX || ARCH_MXC)
|
||||
select SERIAL_CORE
|
||||
select RATIONAL
|
||||
help
|
||||
If you have a machine based on a Motorola IMX CPU you
|
||||
can enable its onboard serial port by enabling this option.
|
||||
@ -1433,4 +1434,11 @@ config SPORT_BAUD_RATE
|
||||
default 19200 if (SERIAL_SPORT_BAUD_RATE_19200)
|
||||
default 9600 if (SERIAL_SPORT_BAUD_RATE_9600)
|
||||
|
||||
config SERIAL_TIMBERDALE
|
||||
tristate "Support for timberdale UART"
|
||||
depends on MFD_TIMBERDALE
|
||||
select SERIAL_CORE
|
||||
---help---
|
||||
Add support for UART controller on timberdale.
|
||||
|
||||
endmenu
|
||||
|
@ -77,3 +77,4 @@ obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
|
||||
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
|
||||
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
|
||||
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
|
||||
obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
|
||||
|
@ -330,6 +330,11 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart)
|
||||
/* Clear TFI bit */
|
||||
UART_PUT_LSR(uart, TFI);
|
||||
#endif
|
||||
/* Anomaly notes:
|
||||
* 05000215 - we always clear ETBEI within last UART TX
|
||||
* interrupt to end a string. It is always set
|
||||
* when start a new tx.
|
||||
*/
|
||||
UART_CLEAR_IER(uart, ETBEI);
|
||||
return;
|
||||
}
|
||||
@ -415,6 +420,7 @@ static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart)
|
||||
set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail));
|
||||
set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
|
||||
set_dma_x_modify(uart->tx_dma_channel, 1);
|
||||
SSYNC();
|
||||
enable_dma(uart->tx_dma_channel);
|
||||
|
||||
UART_SET_IER(uart, ETBEI);
|
||||
@ -473,27 +479,41 @@ static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart)
|
||||
void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
|
||||
{
|
||||
int x_pos, pos;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&uart->port.lock, flags);
|
||||
dma_disable_irq(uart->rx_dma_channel);
|
||||
spin_lock_bh(&uart->port.lock);
|
||||
|
||||
/* 2D DMA RX buffer ring is used. Because curr_y_count and
|
||||
* curr_x_count can't be read as an atomic operation,
|
||||
* curr_y_count should be read before curr_x_count. When
|
||||
* curr_x_count is read, curr_y_count may already indicate
|
||||
* next buffer line. But, the position calculated here is
|
||||
* still indicate the old line. The wrong position data may
|
||||
* be smaller than current buffer tail, which cause garbages
|
||||
* are received if it is not prohibit.
|
||||
*/
|
||||
uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
|
||||
x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
|
||||
uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
|
||||
if (uart->rx_dma_nrows == DMA_RX_YCOUNT)
|
||||
if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0)
|
||||
uart->rx_dma_nrows = 0;
|
||||
x_pos = DMA_RX_XCOUNT - x_pos;
|
||||
if (x_pos == DMA_RX_XCOUNT)
|
||||
x_pos = 0;
|
||||
|
||||
pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos;
|
||||
if (pos != uart->rx_dma_buf.tail) {
|
||||
/* Ignore receiving data if new position is in the same line of
|
||||
* current buffer tail and small.
|
||||
*/
|
||||
if (pos > uart->rx_dma_buf.tail ||
|
||||
uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) {
|
||||
uart->rx_dma_buf.head = pos;
|
||||
bfin_serial_dma_rx_chars(uart);
|
||||
uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&uart->port.lock, flags);
|
||||
spin_unlock_bh(&uart->port.lock);
|
||||
dma_enable_irq(uart->rx_dma_channel);
|
||||
|
||||
mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES);
|
||||
}
|
||||
@ -514,6 +534,11 @@ static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id)
|
||||
if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) {
|
||||
disable_dma(uart->tx_dma_channel);
|
||||
clear_dma_irqstat(uart->tx_dma_channel);
|
||||
/* Anomaly notes:
|
||||
* 05000215 - we always clear ETBEI within last UART TX
|
||||
* interrupt to end a string. It is always set
|
||||
* when start a new tx.
|
||||
*/
|
||||
UART_CLEAR_IER(uart, ETBEI);
|
||||
xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
|
||||
uart->port.icount.tx += uart->tx_count;
|
||||
@ -532,11 +557,26 @@ static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id)
|
||||
{
|
||||
struct bfin_serial_port *uart = dev_id;
|
||||
unsigned short irqstat;
|
||||
int x_pos, pos;
|
||||
|
||||
spin_lock(&uart->port.lock);
|
||||
irqstat = get_dma_curr_irqstat(uart->rx_dma_channel);
|
||||
clear_dma_irqstat(uart->rx_dma_channel);
|
||||
bfin_serial_dma_rx_chars(uart);
|
||||
|
||||
uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
|
||||
x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
|
||||
uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
|
||||
if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0)
|
||||
uart->rx_dma_nrows = 0;
|
||||
|
||||
pos = uart->rx_dma_nrows * DMA_RX_XCOUNT;
|
||||
if (pos > uart->rx_dma_buf.tail ||
|
||||
uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) {
|
||||
uart->rx_dma_buf.head = pos;
|
||||
bfin_serial_dma_rx_chars(uart);
|
||||
uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
|
||||
}
|
||||
|
||||
spin_unlock(&uart->port.lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -789,8 +829,16 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
__func__);
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
lcr |= STB;
|
||||
/* Anomaly notes:
|
||||
* 05000231 - STOP bit is always set to 1 whatever the user is set.
|
||||
*/
|
||||
if (termios->c_cflag & CSTOPB) {
|
||||
if (ANOMALY_05000231)
|
||||
printk(KERN_WARNING "STOP bits other than 1 is not "
|
||||
"supported in case of anomaly 05000231.\n");
|
||||
else
|
||||
lcr |= STB;
|
||||
}
|
||||
if (termios->c_cflag & PARENB)
|
||||
lcr |= PEN;
|
||||
if (!(termios->c_cflag & PARODD))
|
||||
@ -940,6 +988,10 @@ static void bfin_serial_reset_irda(struct uart_port *port)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
/* Anomaly notes:
|
||||
* 05000099 - Because we only use THRE in poll_put and DR in poll_get,
|
||||
* losing other bits of UART_LSR is not a problem here.
|
||||
*/
|
||||
static void bfin_serial_poll_put_char(struct uart_port *port, unsigned char chr)
|
||||
{
|
||||
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
|
||||
@ -1245,12 +1297,17 @@ static __init void early_serial_write(struct console *con, const char *s,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This should have a .setup or .early_setup in it, but then things get called
|
||||
* without the command line options, and the baud rate gets messed up - so
|
||||
* don't let the common infrastructure play with things. (see calls to setup
|
||||
* & earlysetup in ./kernel/printk.c:register_console()
|
||||
*/
|
||||
static struct __initdata console bfin_early_serial_console = {
|
||||
.name = "early_BFuart",
|
||||
.write = early_serial_write,
|
||||
.device = uart_console_device,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.setup = bfin_serial_console_setup,
|
||||
.index = -1,
|
||||
.data = &bfin_serial_reg,
|
||||
};
|
||||
|
@ -101,15 +101,16 @@ static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value)
|
||||
{
|
||||
pr_debug("%s value:%x\n", __func__, value);
|
||||
/* Place a Start and Stop bit */
|
||||
__asm__ volatile (
|
||||
"R2 = b#01111111100;\n\t"
|
||||
"R3 = b#10000000001;\n\t"
|
||||
"%0 <<= 2;\n\t"
|
||||
"%0 = %0 & R2;\n\t"
|
||||
"%0 = %0 | R3;\n\t"
|
||||
:"=r"(value)
|
||||
:"0"(value)
|
||||
:"R2", "R3");
|
||||
__asm__ __volatile__ (
|
||||
"R2 = b#01111111100;"
|
||||
"R3 = b#10000000001;"
|
||||
"%0 <<= 2;"
|
||||
"%0 = %0 & R2;"
|
||||
"%0 = %0 | R3;"
|
||||
: "=d"(value)
|
||||
: "d"(value)
|
||||
: "ASTAT", "R2", "R3"
|
||||
);
|
||||
pr_debug("%s value:%x\n", __func__, value);
|
||||
|
||||
SPORT_PUT_TX(up, value);
|
||||
@ -118,27 +119,30 @@ static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value)
|
||||
static inline unsigned int rx_one_byte(struct sport_uart_port *up)
|
||||
{
|
||||
unsigned int value, extract;
|
||||
u32 tmp_mask1, tmp_mask2, tmp_shift, tmp;
|
||||
|
||||
value = SPORT_GET_RX32(up);
|
||||
pr_debug("%s value:%x\n", __func__, value);
|
||||
|
||||
/* Extract 8 bits data */
|
||||
__asm__ volatile (
|
||||
"R5 = 0;\n\t"
|
||||
"P0 = 8;\n\t"
|
||||
"R1 = 0x1801(Z);\n\t"
|
||||
"R3 = 0x0300(Z);\n\t"
|
||||
"R4 = 0;\n\t"
|
||||
"LSETUP(loop_s, loop_e) LC0 = P0;\nloop_s:\t"
|
||||
"R2 = extract(%1, R1.L)(Z);\n\t"
|
||||
"R2 <<= R4;\n\t"
|
||||
"R5 = R5 | R2;\n\t"
|
||||
"R1 = R1 - R3;\nloop_e:\t"
|
||||
"R4 += 1;\n\t"
|
||||
"%0 = R5;\n\t"
|
||||
:"=r"(extract)
|
||||
:"r"(value)
|
||||
:"P0", "R1", "R2","R3","R4", "R5");
|
||||
__asm__ __volatile__ (
|
||||
"%[extr] = 0;"
|
||||
"%[mask1] = 0x1801(Z);"
|
||||
"%[mask2] = 0x0300(Z);"
|
||||
"%[shift] = 0;"
|
||||
"LSETUP(.Lloop_s, .Lloop_e) LC0 = %[lc];"
|
||||
".Lloop_s:"
|
||||
"%[tmp] = extract(%[val], %[mask1].L)(Z);"
|
||||
"%[tmp] <<= %[shift];"
|
||||
"%[extr] = %[extr] | %[tmp];"
|
||||
"%[mask1] = %[mask1] - %[mask2];"
|
||||
".Lloop_e:"
|
||||
"%[shift] += 1;"
|
||||
: [val]"=d"(value), [extr]"=d"(extract), [shift]"=d"(tmp_shift), [tmp]"=d"(tmp),
|
||||
[mask1]"=d"(tmp_mask1), [mask2]"=d"(tmp_mask2)
|
||||
: "d"(value), [lc]"a"(8)
|
||||
: "ASTAT", "LB0", "LC0", "LT0"
|
||||
);
|
||||
|
||||
pr_debug(" extract:%x\n", extract);
|
||||
return extract;
|
||||
@ -149,7 +153,7 @@ static int sport_uart_setup(struct sport_uart_port *up, int sclk, int baud_rate)
|
||||
int tclkdiv, tfsdiv, rclkdiv;
|
||||
|
||||
/* Set TCR1 and TCR2 */
|
||||
SPORT_PUT_TCR1(up, (LTFS | ITFS | TFSR | TLSBIT | ITCLK));
|
||||
SPORT_PUT_TCR1(up, (LATFS | ITFS | TFSR | TLSBIT | ITCLK));
|
||||
SPORT_PUT_TCR2(up, 10);
|
||||
pr_debug("%s TCR1:%x, TCR2:%x\n", __func__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up));
|
||||
|
||||
@ -419,7 +423,7 @@ static void sport_shutdown(struct uart_port *port)
|
||||
}
|
||||
|
||||
static void sport_set_termios(struct uart_port *port,
|
||||
struct termios *termios, struct termios *old)
|
||||
struct ktermios *termios, struct ktermios *old)
|
||||
{
|
||||
pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag);
|
||||
uart_update_timeout(port, CS8 ,port->uartclk);
|
||||
|
@ -137,7 +137,12 @@ static LIST_HEAD(icom_adapter_head);
|
||||
static spinlock_t icom_lock;
|
||||
|
||||
#ifdef ICOM_TRACE
|
||||
static inline void trace(struct icom_port *, char *, unsigned long) {};
|
||||
static inline void trace(struct icom_port *icom_port, char *trace_pt,
|
||||
unsigned long trace_data)
|
||||
{
|
||||
dev_info(&icom_port->adapter->pci_dev->dev, ":%d:%s - %lx\n",
|
||||
icom_port->port, trace_pt, trace_data);
|
||||
}
|
||||
#else
|
||||
static inline void trace(struct icom_port *icom_port, char *trace_pt, unsigned long trace_data) {};
|
||||
#endif
|
||||
@ -408,7 +413,7 @@ static void load_code(struct icom_port *icom_port)
|
||||
release_firmware(fw);
|
||||
|
||||
/* Set Hardware level */
|
||||
if ((icom_port->adapter->version | ADAPTER_V2) == ADAPTER_V2)
|
||||
if (icom_port->adapter->version == ADAPTER_V2)
|
||||
writeb(V2_HARDWARE, &(icom_port->dram->misc_flags));
|
||||
|
||||
/* Start the processor in Adapter */
|
||||
@ -861,7 +866,7 @@ static irqreturn_t icom_interrupt(int irq, void *dev_id)
|
||||
/* find icom_port for this interrupt */
|
||||
icom_adapter = (struct icom_adapter *) dev_id;
|
||||
|
||||
if ((icom_adapter->version | ADAPTER_V2) == ADAPTER_V2) {
|
||||
if (icom_adapter->version == ADAPTER_V2) {
|
||||
int_reg = icom_adapter->base_addr + 0x8024;
|
||||
|
||||
adapter_interrupts = readl(int_reg);
|
||||
@ -1647,15 +1652,6 @@ static void __exit icom_exit(void)
|
||||
module_init(icom_init);
|
||||
module_exit(icom_exit);
|
||||
|
||||
#ifdef ICOM_TRACE
|
||||
static inline void trace(struct icom_port *icom_port, char *trace_pt,
|
||||
unsigned long trace_data)
|
||||
{
|
||||
dev_info(&icom_port->adapter->pci_dev->dev, ":%d:%s - %lx\n",
|
||||
icom_port->port, trace_pt, trace_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Michael Anderson <mjanders@us.ibm.com>");
|
||||
MODULE_DESCRIPTION("IBM iSeries Serial IOA driver");
|
||||
MODULE_SUPPORTED_DEVICE
|
||||
|
@ -8,6 +8,9 @@
|
||||
* Author: Sascha Hauer <sascha@saschahauer.de>
|
||||
* Copyright (C) 2004 Pengutronix
|
||||
*
|
||||
* Copyright (C) 2009 emlix GmbH
|
||||
* Author: Fabian Godehardt (added IrDA support for iMX)
|
||||
*
|
||||
* 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
|
||||
@ -41,6 +44,8 @@
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/rational.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
@ -148,6 +153,7 @@
|
||||
#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */
|
||||
#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */
|
||||
#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */
|
||||
#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7)
|
||||
#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */
|
||||
#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */
|
||||
#define USR1_RTSS (1<<14) /* RTS pin status */
|
||||
@ -211,10 +217,20 @@ struct imx_port {
|
||||
struct timer_list timer;
|
||||
unsigned int old_status;
|
||||
int txirq,rxirq,rtsirq;
|
||||
int have_rtscts:1;
|
||||
unsigned int have_rtscts:1;
|
||||
unsigned int use_irda:1;
|
||||
unsigned int irda_inv_rx:1;
|
||||
unsigned int irda_inv_tx:1;
|
||||
unsigned short trcv_delay; /* transceiver delay */
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IRDA
|
||||
#define USE_IRDA(sport) ((sport)->use_irda)
|
||||
#else
|
||||
#define USE_IRDA(sport) (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Handle any change of modem status signal since we were last called.
|
||||
*/
|
||||
@ -268,6 +284,48 @@ static void imx_stop_tx(struct uart_port *port)
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned long temp;
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
/* half duplex - wait for end of transmission */
|
||||
int n = 256;
|
||||
while ((--n > 0) &&
|
||||
!(readl(sport->port.membase + USR2) & USR2_TXDC)) {
|
||||
udelay(5);
|
||||
barrier();
|
||||
}
|
||||
/*
|
||||
* irda transceiver - wait a bit more to avoid
|
||||
* cutoff, hardware dependent
|
||||
*/
|
||||
udelay(sport->trcv_delay);
|
||||
|
||||
/*
|
||||
* half duplex - reactivate receive mode,
|
||||
* flush receive pipe echo crap
|
||||
*/
|
||||
if (readl(sport->port.membase + USR2) & USR2_TXDC) {
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp &= ~(UCR1_TXMPTYEN | UCR1_TRDYEN);
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
temp &= ~(UCR4_TCEN);
|
||||
writel(temp, sport->port.membase + UCR4);
|
||||
|
||||
while (readl(sport->port.membase + URXD0) &
|
||||
URXD_CHARRDY)
|
||||
barrier();
|
||||
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp |= UCR1_RRDYEN;
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
temp |= UCR4_DREN;
|
||||
writel(temp, sport->port.membase + UCR4);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
|
||||
}
|
||||
@ -302,13 +360,15 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
|
||||
/* send xmit->buf[xmit->tail]
|
||||
* out the port here */
|
||||
writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
|
||||
xmit->tail = (xmit->tail + 1) &
|
||||
(UART_XMIT_SIZE - 1);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
sport->port.icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
}
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(&sport->port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
imx_stop_tx(&sport->port);
|
||||
}
|
||||
@ -321,9 +381,30 @@ static void imx_start_tx(struct uart_port *port)
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned long temp;
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
/* half duplex in IrDA mode; have to disable receive mode */
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
temp &= ~(UCR4_DREN);
|
||||
writel(temp, sport->port.membase + UCR4);
|
||||
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp &= ~(UCR1_RRDYEN);
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
}
|
||||
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp |= UCR1_TRDYEN;
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
temp |= UCR4_TCEN;
|
||||
writel(temp, sport->port.membase + UCR4);
|
||||
}
|
||||
|
||||
if (readl(sport->port.membase + UTS) & UTS_TXEMPTY)
|
||||
imx_transmit_buffer(sport);
|
||||
}
|
||||
@ -395,8 +476,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uart_handle_sysrq_char
|
||||
(&sport->port, (unsigned char)rx))
|
||||
if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
|
||||
continue;
|
||||
|
||||
if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) {
|
||||
@ -471,26 +551,26 @@ static unsigned int imx_tx_empty(struct uart_port *port)
|
||||
*/
|
||||
static unsigned int imx_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
|
||||
|
||||
if (readl(sport->port.membase + USR1) & USR1_RTSS)
|
||||
tmp |= TIOCM_CTS;
|
||||
if (readl(sport->port.membase + USR1) & USR1_RTSS)
|
||||
tmp |= TIOCM_CTS;
|
||||
|
||||
if (readl(sport->port.membase + UCR2) & UCR2_CTS)
|
||||
tmp |= TIOCM_RTS;
|
||||
if (readl(sport->port.membase + UCR2) & UCR2_CTS)
|
||||
tmp |= TIOCM_RTS;
|
||||
|
||||
return tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned long temp;
|
||||
|
||||
temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
|
||||
|
||||
if (mctrl & TIOCM_RTS)
|
||||
if (mctrl & TIOCM_RTS)
|
||||
temp |= UCR2_CTS;
|
||||
|
||||
writel(temp, sport->port.membase + UCR2);
|
||||
@ -534,12 +614,7 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
|
||||
if(!ufcr_rfdiv)
|
||||
ufcr_rfdiv = 1;
|
||||
|
||||
if(ufcr_rfdiv >= 7)
|
||||
ufcr_rfdiv = 6;
|
||||
else
|
||||
ufcr_rfdiv = 6 - ufcr_rfdiv;
|
||||
|
||||
val |= UFCR_RFDIV & (ufcr_rfdiv << 7);
|
||||
val |= UFCR_RFDIV_REG(ufcr_rfdiv);
|
||||
|
||||
writel(val, sport->port.membase + UFCR);
|
||||
|
||||
@ -558,8 +633,24 @@ static int imx_startup(struct uart_port *port)
|
||||
* requesting IRQs
|
||||
*/
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
|
||||
if (USE_IRDA(sport))
|
||||
temp |= UCR4_IRSC;
|
||||
|
||||
writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
/* reset fifo's and state machines */
|
||||
int i = 100;
|
||||
temp = readl(sport->port.membase + UCR2);
|
||||
temp &= ~UCR2_SRST;
|
||||
writel(temp, sport->port.membase + UCR2);
|
||||
while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) &&
|
||||
(--i > 0)) {
|
||||
udelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate the IRQ(s) i.MX1 has three interrupts whereas later
|
||||
* chips only have one interrupt.
|
||||
@ -575,12 +666,16 @@ static int imx_startup(struct uart_port *port)
|
||||
if (retval)
|
||||
goto error_out2;
|
||||
|
||||
retval = request_irq(sport->rtsirq, imx_rtsint,
|
||||
(sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||
DRIVER_NAME, sport);
|
||||
if (retval)
|
||||
goto error_out3;
|
||||
/* do not use RTS IRQ on IrDA */
|
||||
if (!USE_IRDA(sport)) {
|
||||
retval = request_irq(sport->rtsirq, imx_rtsint,
|
||||
(sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_TRIGGER_RISING,
|
||||
DRIVER_NAME, sport);
|
||||
if (retval)
|
||||
goto error_out3;
|
||||
}
|
||||
} else {
|
||||
retval = request_irq(sport->port.irq, imx_int, 0,
|
||||
DRIVER_NAME, sport);
|
||||
@ -597,18 +692,49 @@ static int imx_startup(struct uart_port *port)
|
||||
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
temp |= UCR1_IREN;
|
||||
temp &= ~(UCR1_RTSDEN);
|
||||
}
|
||||
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
|
||||
temp = readl(sport->port.membase + UCR2);
|
||||
temp |= (UCR2_RXEN | UCR2_TXEN);
|
||||
writel(temp, sport->port.membase + UCR2);
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
/* clear RX-FIFO */
|
||||
int i = 64;
|
||||
while ((--i > 0) &&
|
||||
(readl(sport->port.membase + URXD0) & URXD_CHARRDY)) {
|
||||
barrier();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3
|
||||
temp = readl(sport->port.membase + UCR3);
|
||||
temp |= UCR3_RXDMUXSEL;
|
||||
writel(temp, sport->port.membase + UCR3);
|
||||
#endif
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
temp = readl(sport->port.membase + UCR4);
|
||||
if (sport->irda_inv_rx)
|
||||
temp |= UCR4_INVR;
|
||||
else
|
||||
temp &= ~(UCR4_INVR);
|
||||
writel(temp | UCR4_DREN, sport->port.membase + UCR4);
|
||||
|
||||
temp = readl(sport->port.membase + UCR3);
|
||||
if (sport->irda_inv_tx)
|
||||
temp |= UCR3_INVT;
|
||||
else
|
||||
temp &= ~(UCR3_INVT);
|
||||
writel(temp, sport->port.membase + UCR3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable modem status interrupts
|
||||
*/
|
||||
@ -616,6 +742,16 @@ static int imx_startup(struct uart_port *port)
|
||||
imx_enable_ms(&sport->port);
|
||||
spin_unlock_irqrestore(&sport->port.lock,flags);
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
struct imxuart_platform_data *pdata;
|
||||
pdata = sport->port.dev->platform_data;
|
||||
sport->irda_inv_rx = pdata->irda_inv_rx;
|
||||
sport->irda_inv_tx = pdata->irda_inv_tx;
|
||||
sport->trcv_delay = pdata->transceiver_delay;
|
||||
if (pdata->irda_enable)
|
||||
pdata->irda_enable(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_out3:
|
||||
@ -633,6 +769,17 @@ static void imx_shutdown(struct uart_port *port)
|
||||
struct imx_port *sport = (struct imx_port *)port;
|
||||
unsigned long temp;
|
||||
|
||||
temp = readl(sport->port.membase + UCR2);
|
||||
temp &= ~(UCR2_TXEN);
|
||||
writel(temp, sport->port.membase + UCR2);
|
||||
|
||||
if (USE_IRDA(sport)) {
|
||||
struct imxuart_platform_data *pdata;
|
||||
pdata = sport->port.dev->platform_data;
|
||||
if (pdata->irda_enable)
|
||||
pdata->irda_enable(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop our timer.
|
||||
*/
|
||||
@ -642,7 +789,8 @@ static void imx_shutdown(struct uart_port *port)
|
||||
* Free the interrupts
|
||||
*/
|
||||
if (sport->txirq > 0) {
|
||||
free_irq(sport->rtsirq, sport);
|
||||
if (!USE_IRDA(sport))
|
||||
free_irq(sport->rtsirq, sport);
|
||||
free_irq(sport->txirq, sport);
|
||||
free_irq(sport->rxirq, sport);
|
||||
} else
|
||||
@ -654,6 +802,9 @@ static void imx_shutdown(struct uart_port *port)
|
||||
|
||||
temp = readl(sport->port.membase + UCR1);
|
||||
temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
|
||||
if (USE_IRDA(sport))
|
||||
temp &= ~(UCR1_IREN);
|
||||
|
||||
writel(temp, sport->port.membase + UCR1);
|
||||
}
|
||||
|
||||
@ -665,7 +816,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
unsigned long flags;
|
||||
unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
|
||||
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
|
||||
unsigned int div, num, denom, ufcr;
|
||||
unsigned int div, ufcr;
|
||||
unsigned long num, denom;
|
||||
uint64_t tdiv64;
|
||||
|
||||
/*
|
||||
* If we don't support modem control lines, don't allow
|
||||
@ -761,38 +914,39 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
sport->port.membase + UCR2);
|
||||
old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
|
||||
|
||||
div = sport->port.uartclk / (baud * 16);
|
||||
if (div > 7)
|
||||
div = 7;
|
||||
if (!div)
|
||||
if (USE_IRDA(sport)) {
|
||||
/*
|
||||
* use maximum available submodule frequency to
|
||||
* avoid missing short pulses due to low sampling rate
|
||||
*/
|
||||
div = 1;
|
||||
|
||||
num = baud;
|
||||
denom = port->uartclk / div / 16;
|
||||
|
||||
/* shift num and denom right until they fit into 16 bits */
|
||||
while (num > 0x10000 || denom > 0x10000) {
|
||||
num >>= 1;
|
||||
denom >>= 1;
|
||||
} else {
|
||||
div = sport->port.uartclk / (baud * 16);
|
||||
if (div > 7)
|
||||
div = 7;
|
||||
if (!div)
|
||||
div = 1;
|
||||
}
|
||||
if (num > 0)
|
||||
num -= 1;
|
||||
if (denom > 0)
|
||||
denom -= 1;
|
||||
|
||||
rational_best_approximation(16 * div * baud, sport->port.uartclk,
|
||||
1 << 16, 1 << 16, &num, &denom);
|
||||
|
||||
tdiv64 = sport->port.uartclk;
|
||||
tdiv64 *= num;
|
||||
do_div(tdiv64, denom * 16 * div);
|
||||
tty_encode_baud_rate(sport->port.info->port.tty,
|
||||
(speed_t)tdiv64, (speed_t)tdiv64);
|
||||
|
||||
num -= 1;
|
||||
denom -= 1;
|
||||
|
||||
ufcr = readl(sport->port.membase + UFCR);
|
||||
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
|
||||
writel(ufcr, sport->port.membase + UFCR);
|
||||
|
||||
writel(num, sport->port.membase + UBIR);
|
||||
writel(denom, sport->port.membase + UBMR);
|
||||
|
||||
if (div == 7)
|
||||
div = 6; /* 6 in RFDIV means divide by 7 */
|
||||
else
|
||||
div = 6 - div;
|
||||
|
||||
ufcr = readl(sport->port.membase + UFCR);
|
||||
ufcr = (ufcr & (~UFCR_RFDIV)) |
|
||||
(div << 7);
|
||||
writel(ufcr, sport->port.membase + UFCR);
|
||||
|
||||
#ifdef ONEMS
|
||||
writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS);
|
||||
#endif
|
||||
@ -1072,22 +1226,22 @@ static struct uart_driver imx_reg = {
|
||||
|
||||
static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct imx_port *sport = platform_get_drvdata(dev);
|
||||
struct imx_port *sport = platform_get_drvdata(dev);
|
||||
|
||||
if (sport)
|
||||
uart_suspend_port(&imx_reg, &sport->port);
|
||||
if (sport)
|
||||
uart_suspend_port(&imx_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_imx_resume(struct platform_device *dev)
|
||||
{
|
||||
struct imx_port *sport = platform_get_drvdata(dev);
|
||||
struct imx_port *sport = platform_get_drvdata(dev);
|
||||
|
||||
if (sport)
|
||||
uart_resume_port(&imx_reg, &sport->port);
|
||||
if (sport)
|
||||
uart_resume_port(&imx_reg, &sport->port);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_imx_probe(struct platform_device *pdev)
|
||||
@ -1143,19 +1297,29 @@ static int serial_imx_probe(struct platform_device *pdev)
|
||||
imx_ports[pdev->id] = sport;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
|
||||
if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
|
||||
sport->have_rtscts = 1;
|
||||
|
||||
#ifdef CONFIG_IRDA
|
||||
if (pdata && (pdata->flags & IMXUART_IRDA))
|
||||
sport->use_irda = 1;
|
||||
#endif
|
||||
|
||||
if (pdata->init) {
|
||||
ret = pdata->init(pdev);
|
||||
if (ret)
|
||||
goto clkput;
|
||||
}
|
||||
|
||||
uart_add_one_port(&imx_reg, &sport->port);
|
||||
ret = uart_add_one_port(&imx_reg, &sport->port);
|
||||
if (ret)
|
||||
goto deinit;
|
||||
platform_set_drvdata(pdev, &sport->port);
|
||||
|
||||
return 0;
|
||||
deinit:
|
||||
if (pdata->exit)
|
||||
pdata->exit(pdev);
|
||||
clkput:
|
||||
clk_put(sport->clk);
|
||||
clk_disable(sport->clk);
|
||||
@ -1193,13 +1357,13 @@ static int serial_imx_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static struct platform_driver serial_imx_driver = {
|
||||
.probe = serial_imx_probe,
|
||||
.remove = serial_imx_remove,
|
||||
.probe = serial_imx_probe,
|
||||
.remove = serial_imx_remove,
|
||||
|
||||
.suspend = serial_imx_suspend,
|
||||
.resume = serial_imx_resume,
|
||||
.driver = {
|
||||
.name = "imx-uart",
|
||||
.name = "imx-uart",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
@ -61,6 +61,7 @@ enum {
|
||||
if ((DBG_##nlevel & jsm_debug)) \
|
||||
dev_printk(KERN_##klevel, pdev->dev, fmt, ## args)
|
||||
|
||||
#define MAXLINES 256
|
||||
#define MAXPORTS 8
|
||||
#define MAX_STOPS_SENT 5
|
||||
|
||||
|
@ -33,6 +33,8 @@
|
||||
|
||||
#include "jsm.h"
|
||||
|
||||
static DECLARE_BITMAP(linemap, MAXLINES);
|
||||
|
||||
static void jsm_carrier(struct jsm_channel *ch);
|
||||
|
||||
static inline int jsm_get_mstat(struct jsm_channel *ch)
|
||||
@ -433,6 +435,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd)
|
||||
int __devinit jsm_uart_port_init(struct jsm_board *brd)
|
||||
{
|
||||
int i;
|
||||
unsigned int line;
|
||||
struct jsm_channel *ch;
|
||||
|
||||
if (!brd)
|
||||
@ -459,9 +462,15 @@ int __devinit jsm_uart_port_init(struct jsm_board *brd)
|
||||
brd->channels[i]->uart_port.membase = brd->re_map_membase;
|
||||
brd->channels[i]->uart_port.fifosize = 16;
|
||||
brd->channels[i]->uart_port.ops = &jsm_ops;
|
||||
brd->channels[i]->uart_port.line = brd->channels[i]->ch_portnum + brd->boardnum * 2;
|
||||
line = find_first_zero_bit(linemap, MAXLINES);
|
||||
if (line >= MAXLINES) {
|
||||
printk(KERN_INFO "jsm: linemap is full, added device failed\n");
|
||||
continue;
|
||||
} else
|
||||
set_bit((int)line, linemap);
|
||||
brd->channels[i]->uart_port.line = line;
|
||||
if (uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port))
|
||||
printk(KERN_INFO "Added device failed\n");
|
||||
printk(KERN_INFO "jsm: add device failed\n");
|
||||
else
|
||||
printk(KERN_INFO "Added device \n");
|
||||
}
|
||||
@ -494,6 +503,7 @@ int jsm_remove_uart_port(struct jsm_board *brd)
|
||||
|
||||
ch = brd->channels[i];
|
||||
|
||||
clear_bit((int)(ch->uart_port.line), linemap);
|
||||
uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port);
|
||||
}
|
||||
|
||||
|
526
drivers/serial/timbuart.c
Normal file
526
drivers/serial/timbuart.c
Normal file
@ -0,0 +1,526 @@
|
||||
/*
|
||||
* timbuart.c timberdale FPGA UART driver
|
||||
* Copyright (c) 2009 Intel Corporation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* Supports:
|
||||
* Timberdale FPGA UART
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
#include "timbuart.h"
|
||||
|
||||
struct timbuart_port {
|
||||
struct uart_port port;
|
||||
struct tasklet_struct tasklet;
|
||||
int usedma;
|
||||
u8 last_ier;
|
||||
struct platform_device *dev;
|
||||
};
|
||||
|
||||
static int baudrates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800,
|
||||
921600, 1843200, 3250000};
|
||||
|
||||
static void timbuart_mctrl_check(struct uart_port *port, u8 isr, u8 *ier);
|
||||
|
||||
static irqreturn_t timbuart_handleinterrupt(int irq, void *devid);
|
||||
|
||||
static void timbuart_stop_rx(struct uart_port *port)
|
||||
{
|
||||
/* spin lock held by upper layer, disable all RX interrupts */
|
||||
u8 ier = ioread8(port->membase + TIMBUART_IER) & ~RXFLAGS;
|
||||
iowrite8(ier, port->membase + TIMBUART_IER);
|
||||
}
|
||||
|
||||
static void timbuart_stop_tx(struct uart_port *port)
|
||||
{
|
||||
/* spinlock held by upper layer, disable TX interrupt */
|
||||
u8 ier = ioread8(port->membase + TIMBUART_IER) & ~TXBAE;
|
||||
iowrite8(ier, port->membase + TIMBUART_IER);
|
||||
}
|
||||
|
||||
static void timbuart_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct timbuart_port *uart =
|
||||
container_of(port, struct timbuart_port, port);
|
||||
|
||||
/* do not transfer anything here -> fire off the tasklet */
|
||||
tasklet_schedule(&uart->tasklet);
|
||||
}
|
||||
|
||||
static void timbuart_flush_buffer(struct uart_port *port)
|
||||
{
|
||||
u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | TIMBUART_CTRL_FLSHTX;
|
||||
|
||||
iowrite8(ctl, port->membase + TIMBUART_CTRL);
|
||||
iowrite8(TXBF, port->membase + TIMBUART_ISR);
|
||||
}
|
||||
|
||||
static void timbuart_rx_chars(struct uart_port *port)
|
||||
{
|
||||
struct tty_struct *tty = port->info->port.tty;
|
||||
|
||||
while (ioread8(port->membase + TIMBUART_ISR) & RXDP) {
|
||||
u8 ch = ioread8(port->membase + TIMBUART_RXFIFO);
|
||||
port->icount.rx++;
|
||||
tty_insert_flip_char(tty, ch, TTY_NORMAL);
|
||||
}
|
||||
|
||||
spin_unlock(&port->lock);
|
||||
tty_flip_buffer_push(port->info->port.tty);
|
||||
spin_lock(&port->lock);
|
||||
|
||||
dev_dbg(port->dev, "%s - total read %d bytes\n",
|
||||
__func__, port->icount.rx);
|
||||
}
|
||||
|
||||
static void timbuart_tx_chars(struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
|
||||
while (!(ioread8(port->membase + TIMBUART_ISR) & TXBF) &&
|
||||
!uart_circ_empty(xmit)) {
|
||||
iowrite8(xmit->buf[xmit->tail],
|
||||
port->membase + TIMBUART_TXFIFO);
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
}
|
||||
|
||||
dev_dbg(port->dev,
|
||||
"%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n",
|
||||
__func__,
|
||||
port->icount.tx,
|
||||
ioread8(port->membase + TIMBUART_CTRL),
|
||||
port->mctrl & TIOCM_RTS,
|
||||
ioread8(port->membase + TIMBUART_BAUDRATE));
|
||||
}
|
||||
|
||||
static void timbuart_handle_tx_port(struct uart_port *port, u8 isr, u8 *ier)
|
||||
{
|
||||
struct timbuart_port *uart =
|
||||
container_of(port, struct timbuart_port, port);
|
||||
struct circ_buf *xmit = &port->info->xmit;
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
|
||||
return;
|
||||
|
||||
if (port->x_char)
|
||||
return;
|
||||
|
||||
if (isr & TXFLAGS) {
|
||||
timbuart_tx_chars(port);
|
||||
/* clear all TX interrupts */
|
||||
iowrite8(TXFLAGS, port->membase + TIMBUART_ISR);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
} else
|
||||
/* Re-enable any tx interrupt */
|
||||
*ier |= uart->last_ier & TXFLAGS;
|
||||
|
||||
/* enable interrupts if there are chars in the transmit buffer,
|
||||
* Or if we delivered some bytes and want the almost empty interrupt
|
||||
* we wake up the upper layer later when we got the interrupt
|
||||
* to give it some time to go out...
|
||||
*/
|
||||
if (!uart_circ_empty(xmit))
|
||||
*ier |= TXBAE;
|
||||
|
||||
dev_dbg(port->dev, "%s - leaving\n", __func__);
|
||||
}
|
||||
|
||||
void timbuart_handle_rx_port(struct uart_port *port, u8 isr, u8 *ier)
|
||||
{
|
||||
if (isr & RXFLAGS) {
|
||||
/* Some RX status is set */
|
||||
if (isr & RXBF) {
|
||||
u8 ctl = ioread8(port->membase + TIMBUART_CTRL) |
|
||||
TIMBUART_CTRL_FLSHRX;
|
||||
iowrite8(ctl, port->membase + TIMBUART_CTRL);
|
||||
port->icount.overrun++;
|
||||
} else if (isr & (RXDP))
|
||||
timbuart_rx_chars(port);
|
||||
|
||||
/* ack all RX interrupts */
|
||||
iowrite8(RXFLAGS, port->membase + TIMBUART_ISR);
|
||||
}
|
||||
|
||||
/* always have the RX interrupts enabled */
|
||||
*ier |= RXBAF | RXBF | RXTT;
|
||||
|
||||
dev_dbg(port->dev, "%s - leaving\n", __func__);
|
||||
}
|
||||
|
||||
void timbuart_tasklet(unsigned long arg)
|
||||
{
|
||||
struct timbuart_port *uart = (struct timbuart_port *)arg;
|
||||
u8 isr, ier = 0;
|
||||
|
||||
spin_lock(&uart->port.lock);
|
||||
|
||||
isr = ioread8(uart->port.membase + TIMBUART_ISR);
|
||||
dev_dbg(uart->port.dev, "%s ISR: %x\n", __func__, isr);
|
||||
|
||||
if (!uart->usedma)
|
||||
timbuart_handle_tx_port(&uart->port, isr, &ier);
|
||||
|
||||
timbuart_mctrl_check(&uart->port, isr, &ier);
|
||||
|
||||
if (!uart->usedma)
|
||||
timbuart_handle_rx_port(&uart->port, isr, &ier);
|
||||
|
||||
iowrite8(ier, uart->port.membase + TIMBUART_IER);
|
||||
|
||||
spin_unlock(&uart->port.lock);
|
||||
dev_dbg(uart->port.dev, "%s leaving\n", __func__);
|
||||
}
|
||||
|
||||
static unsigned int timbuart_tx_empty(struct uart_port *port)
|
||||
{
|
||||
u8 isr = ioread8(port->membase + TIMBUART_ISR);
|
||||
|
||||
return (isr & TXBAE) ? TIOCSER_TEMT : 0;
|
||||
}
|
||||
|
||||
static unsigned int timbuart_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
u8 cts = ioread8(port->membase + TIMBUART_CTRL);
|
||||
dev_dbg(port->dev, "%s - cts %x\n", __func__, cts);
|
||||
|
||||
if (cts & TIMBUART_CTRL_CTS)
|
||||
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
|
||||
else
|
||||
return TIOCM_DSR | TIOCM_CAR;
|
||||
}
|
||||
|
||||
static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
dev_dbg(port->dev, "%s - %x\n", __func__, mctrl);
|
||||
|
||||
if (mctrl & TIOCM_RTS)
|
||||
iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL);
|
||||
else
|
||||
iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL);
|
||||
}
|
||||
|
||||
static void timbuart_mctrl_check(struct uart_port *port, u8 isr, u8 *ier)
|
||||
{
|
||||
unsigned int cts;
|
||||
|
||||
if (isr & CTS_DELTA) {
|
||||
/* ack */
|
||||
iowrite8(CTS_DELTA, port->membase + TIMBUART_ISR);
|
||||
cts = timbuart_get_mctrl(port);
|
||||
uart_handle_cts_change(port, cts & TIOCM_CTS);
|
||||
wake_up_interruptible(&port->info->delta_msr_wait);
|
||||
}
|
||||
|
||||
*ier |= CTS_DELTA;
|
||||
}
|
||||
|
||||
static void timbuart_enable_ms(struct uart_port *port)
|
||||
{
|
||||
/* N/A */
|
||||
}
|
||||
|
||||
static void timbuart_break_ctl(struct uart_port *port, int ctl)
|
||||
{
|
||||
/* N/A */
|
||||
}
|
||||
|
||||
static int timbuart_startup(struct uart_port *port)
|
||||
{
|
||||
struct timbuart_port *uart =
|
||||
container_of(port, struct timbuart_port, port);
|
||||
|
||||
dev_dbg(port->dev, "%s\n", __func__);
|
||||
|
||||
iowrite8(TIMBUART_CTRL_FLSHRX, port->membase + TIMBUART_CTRL);
|
||||
iowrite8(0xff, port->membase + TIMBUART_ISR);
|
||||
/* Enable all but TX interrupts */
|
||||
iowrite8(RXBAF | RXBF | RXTT | CTS_DELTA,
|
||||
port->membase + TIMBUART_IER);
|
||||
|
||||
return request_irq(port->irq, timbuart_handleinterrupt, IRQF_SHARED,
|
||||
"timb-uart", uart);
|
||||
}
|
||||
|
||||
static void timbuart_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct timbuart_port *uart =
|
||||
container_of(port, struct timbuart_port, port);
|
||||
dev_dbg(port->dev, "%s\n", __func__);
|
||||
free_irq(port->irq, uart);
|
||||
iowrite8(0, port->membase + TIMBUART_IER);
|
||||
}
|
||||
|
||||
static int get_bindex(int baud)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(baudrates); i++)
|
||||
if (baud <= baudrates[i])
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void timbuart_set_termios(struct uart_port *port,
|
||||
struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned int baud;
|
||||
short bindex;
|
||||
unsigned long flags;
|
||||
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
|
||||
bindex = get_bindex(baud);
|
||||
dev_dbg(port->dev, "%s - bindex %d\n", __func__, bindex);
|
||||
|
||||
if (bindex < 0)
|
||||
bindex = 0;
|
||||
baud = baudrates[bindex];
|
||||
|
||||
/* The serial layer calls into this once with old = NULL when setting
|
||||
up initially */
|
||||
if (old)
|
||||
tty_termios_copy_hw(termios, old);
|
||||
tty_termios_encode_baud_rate(termios, baud, baud);
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
iowrite8((u8)bindex, port->membase + TIMBUART_BAUDRATE);
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char *timbuart_type(struct uart_port *port)
|
||||
{
|
||||
return port->type == PORT_UNKNOWN ? "timbuart" : NULL;
|
||||
}
|
||||
|
||||
/* We do not request/release mappings of the registers here,
|
||||
* currently it's done in the proble function.
|
||||
*/
|
||||
static void timbuart_release_port(struct uart_port *port)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(port->dev);
|
||||
int size =
|
||||
resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0));
|
||||
|
||||
if (port->flags & UPF_IOREMAP) {
|
||||
iounmap(port->membase);
|
||||
port->membase = NULL;
|
||||
}
|
||||
|
||||
release_mem_region(port->mapbase, size);
|
||||
}
|
||||
|
||||
static int timbuart_request_port(struct uart_port *port)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(port->dev);
|
||||
int size =
|
||||
resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0));
|
||||
|
||||
if (!request_mem_region(port->mapbase, size, "timb-uart"))
|
||||
return -EBUSY;
|
||||
|
||||
if (port->flags & UPF_IOREMAP) {
|
||||
port->membase = ioremap(port->mapbase, size);
|
||||
if (port->membase == NULL) {
|
||||
release_mem_region(port->mapbase, size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t timbuart_handleinterrupt(int irq, void *devid)
|
||||
{
|
||||
struct timbuart_port *uart = (struct timbuart_port *)devid;
|
||||
|
||||
if (ioread8(uart->port.membase + TIMBUART_IPR)) {
|
||||
uart->last_ier = ioread8(uart->port.membase + TIMBUART_IER);
|
||||
|
||||
/* disable interrupts, the tasklet enables them again */
|
||||
iowrite8(0, uart->port.membase + TIMBUART_IER);
|
||||
|
||||
/* fire off bottom half */
|
||||
tasklet_schedule(&uart->tasklet);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
} else
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure/autoconfigure the port.
|
||||
*/
|
||||
static void timbuart_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE) {
|
||||
port->type = PORT_TIMBUART;
|
||||
timbuart_request_port(port);
|
||||
}
|
||||
}
|
||||
|
||||
static int timbuart_verify_port(struct uart_port *port,
|
||||
struct serial_struct *ser)
|
||||
{
|
||||
/* we don't want the core code to modify any port params */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct uart_ops timbuart_ops = {
|
||||
.tx_empty = timbuart_tx_empty,
|
||||
.set_mctrl = timbuart_set_mctrl,
|
||||
.get_mctrl = timbuart_get_mctrl,
|
||||
.stop_tx = timbuart_stop_tx,
|
||||
.start_tx = timbuart_start_tx,
|
||||
.flush_buffer = timbuart_flush_buffer,
|
||||
.stop_rx = timbuart_stop_rx,
|
||||
.enable_ms = timbuart_enable_ms,
|
||||
.break_ctl = timbuart_break_ctl,
|
||||
.startup = timbuart_startup,
|
||||
.shutdown = timbuart_shutdown,
|
||||
.set_termios = timbuart_set_termios,
|
||||
.type = timbuart_type,
|
||||
.release_port = timbuart_release_port,
|
||||
.request_port = timbuart_request_port,
|
||||
.config_port = timbuart_config_port,
|
||||
.verify_port = timbuart_verify_port
|
||||
};
|
||||
|
||||
static struct uart_driver timbuart_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = "timberdale_uart",
|
||||
.dev_name = "ttyTU",
|
||||
.major = TIMBUART_MAJOR,
|
||||
.minor = TIMBUART_MINOR,
|
||||
.nr = 1
|
||||
};
|
||||
|
||||
static int timbuart_probe(struct platform_device *dev)
|
||||
{
|
||||
int err;
|
||||
struct timbuart_port *uart;
|
||||
struct resource *iomem;
|
||||
|
||||
dev_dbg(&dev->dev, "%s\n", __func__);
|
||||
|
||||
uart = kzalloc(sizeof(*uart), GFP_KERNEL);
|
||||
if (!uart) {
|
||||
err = -EINVAL;
|
||||
goto err_mem;
|
||||
}
|
||||
|
||||
uart->usedma = 0;
|
||||
|
||||
uart->port.uartclk = 3250000 * 16;
|
||||
uart->port.fifosize = TIMBUART_FIFO_SIZE;
|
||||
uart->port.regshift = 2;
|
||||
uart->port.iotype = UPIO_MEM;
|
||||
uart->port.ops = &timbuart_ops;
|
||||
uart->port.irq = 0;
|
||||
uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP;
|
||||
uart->port.line = 0;
|
||||
uart->port.dev = &dev->dev;
|
||||
|
||||
iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
if (!iomem) {
|
||||
err = -ENOMEM;
|
||||
goto err_register;
|
||||
}
|
||||
uart->port.mapbase = iomem->start;
|
||||
uart->port.membase = NULL;
|
||||
|
||||
uart->port.irq = platform_get_irq(dev, 0);
|
||||
if (uart->port.irq < 0) {
|
||||
err = -EINVAL;
|
||||
goto err_register;
|
||||
}
|
||||
|
||||
tasklet_init(&uart->tasklet, timbuart_tasklet, (unsigned long)uart);
|
||||
|
||||
err = uart_register_driver(&timbuart_driver);
|
||||
if (err)
|
||||
goto err_register;
|
||||
|
||||
err = uart_add_one_port(&timbuart_driver, &uart->port);
|
||||
if (err)
|
||||
goto err_add_port;
|
||||
|
||||
platform_set_drvdata(dev, uart);
|
||||
|
||||
return 0;
|
||||
|
||||
err_add_port:
|
||||
uart_unregister_driver(&timbuart_driver);
|
||||
err_register:
|
||||
kfree(uart);
|
||||
err_mem:
|
||||
printk(KERN_ERR "timberdale: Failed to register Timberdale UART: %d\n",
|
||||
err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int timbuart_remove(struct platform_device *dev)
|
||||
{
|
||||
struct timbuart_port *uart = platform_get_drvdata(dev);
|
||||
|
||||
tasklet_kill(&uart->tasklet);
|
||||
uart_remove_one_port(&timbuart_driver, &uart->port);
|
||||
uart_unregister_driver(&timbuart_driver);
|
||||
kfree(uart);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver timbuart_platform_driver = {
|
||||
.driver = {
|
||||
.name = "timb-uart",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = timbuart_probe,
|
||||
.remove = timbuart_remove,
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
static int __init timbuart_init(void)
|
||||
{
|
||||
return platform_driver_register(&timbuart_platform_driver);
|
||||
}
|
||||
|
||||
static void __exit timbuart_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&timbuart_platform_driver);
|
||||
}
|
||||
|
||||
module_init(timbuart_init);
|
||||
module_exit(timbuart_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Timberdale UART driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:timb-uart");
|
||||
|
58
drivers/serial/timbuart.h
Normal file
58
drivers/serial/timbuart.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* timbuart.c timberdale FPGA GPIO driver
|
||||
* Copyright (c) 2009 Intel Corporation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* Supports:
|
||||
* Timberdale FPGA UART
|
||||
*/
|
||||
|
||||
#ifndef _TIMBUART_H
|
||||
#define _TIMBUART_H
|
||||
|
||||
#define TIMBUART_FIFO_SIZE 2048
|
||||
|
||||
#define TIMBUART_RXFIFO 0x08
|
||||
#define TIMBUART_TXFIFO 0x0c
|
||||
#define TIMBUART_IER 0x10
|
||||
#define TIMBUART_IPR 0x14
|
||||
#define TIMBUART_ISR 0x18
|
||||
#define TIMBUART_CTRL 0x1c
|
||||
#define TIMBUART_BAUDRATE 0x20
|
||||
|
||||
#define TIMBUART_CTRL_RTS 0x01
|
||||
#define TIMBUART_CTRL_CTS 0x02
|
||||
#define TIMBUART_CTRL_FLSHTX 0x40
|
||||
#define TIMBUART_CTRL_FLSHRX 0x80
|
||||
|
||||
#define TXBF 0x01
|
||||
#define TXBAE 0x02
|
||||
#define CTS_DELTA 0x04
|
||||
#define RXDP 0x08
|
||||
#define RXBAF 0x10
|
||||
#define RXBF 0x20
|
||||
#define RXTT 0x40
|
||||
#define RXBNAE 0x80
|
||||
#define TXBE 0x100
|
||||
|
||||
#define RXFLAGS (RXDP | RXBAF | RXBF | RXTT | RXBNAE)
|
||||
#define TXFLAGS (TXBF | TXBAE)
|
||||
|
||||
#define TIMBUART_MAJOR 204
|
||||
#define TIMBUART_MINOR 192
|
||||
|
||||
#endif /* _TIMBUART_H */
|
||||
|
@ -16,7 +16,8 @@
|
||||
* v0.9 - thorough cleaning, URBification, almost a rewrite
|
||||
* v0.10 - some more cleanups
|
||||
* v0.11 - fixed flow control, read error doesn't stop reads
|
||||
* v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced
|
||||
* v0.12 - added TIOCM ioctls, added break handling, made struct acm
|
||||
* kmalloced
|
||||
* v0.13 - added termios, added hangup
|
||||
* v0.14 - sized down struct acm
|
||||
* v0.15 - fixed flow control again - characters could be lost
|
||||
@ -62,7 +63,7 @@
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
#include <asm/byteorder.h>
|
||||
@ -87,7 +88,10 @@ static struct acm *acm_table[ACM_TTY_MINORS];
|
||||
|
||||
static DEFINE_MUTEX(open_mutex);
|
||||
|
||||
#define ACM_READY(acm) (acm && acm->dev && acm->used)
|
||||
#define ACM_READY(acm) (acm && acm->dev && acm->port.count)
|
||||
|
||||
static const struct tty_port_operations acm_port_ops = {
|
||||
};
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#define verbose 1
|
||||
@ -99,13 +103,15 @@ static DEFINE_MUTEX(open_mutex);
|
||||
* Functions for ACM control messages.
|
||||
*/
|
||||
|
||||
static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len)
|
||||
static int acm_ctrl_msg(struct acm *acm, int request, int value,
|
||||
void *buf, int len)
|
||||
{
|
||||
int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
|
||||
request, USB_RT_ACM, value,
|
||||
acm->control->altsetting[0].desc.bInterfaceNumber,
|
||||
buf, len, 5000);
|
||||
dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
|
||||
dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d",
|
||||
request, value, len, retval);
|
||||
return retval < 0 ? retval : 0;
|
||||
}
|
||||
|
||||
@ -150,9 +156,8 @@ static int acm_wb_is_avail(struct acm *acm)
|
||||
|
||||
n = ACM_NW;
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
for (i = 0; i < ACM_NW; i++) {
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
n -= acm->wb[i].use;
|
||||
}
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return n;
|
||||
}
|
||||
@ -183,7 +188,8 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
|
||||
wb->urb->transfer_buffer_length = wb->len;
|
||||
wb->urb->dev = acm->dev;
|
||||
|
||||
if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {
|
||||
rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
|
||||
if (rc < 0) {
|
||||
dbg("usb_submit_urb(write bulk) failed: %d", rc);
|
||||
acm_write_done(acm, wb);
|
||||
}
|
||||
@ -262,6 +268,7 @@ static void acm_ctrl_irq(struct urb *urb)
|
||||
{
|
||||
struct acm *acm = urb->context;
|
||||
struct usb_cdc_notification *dr = urb->transfer_buffer;
|
||||
struct tty_struct *tty;
|
||||
unsigned char *data;
|
||||
int newctrl;
|
||||
int retval;
|
||||
@ -287,40 +294,45 @@ static void acm_ctrl_irq(struct urb *urb)
|
||||
|
||||
data = (unsigned char *)(dr + 1);
|
||||
switch (dr->bNotificationType) {
|
||||
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
|
||||
dbg("%s network", dr->wValue ?
|
||||
"connected to" : "disconnected from");
|
||||
break;
|
||||
|
||||
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
|
||||
case USB_CDC_NOTIFY_SERIAL_STATE:
|
||||
tty = tty_port_tty_get(&acm->port);
|
||||
newctrl = get_unaligned_le16(data);
|
||||
|
||||
dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
|
||||
break;
|
||||
|
||||
case USB_CDC_NOTIFY_SERIAL_STATE:
|
||||
|
||||
newctrl = get_unaligned_le16(data);
|
||||
|
||||
if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
|
||||
if (tty) {
|
||||
if (!acm->clocal &&
|
||||
(acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
|
||||
dbg("calling hangup");
|
||||
tty_hangup(acm->tty);
|
||||
tty_hangup(tty);
|
||||
}
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
acm->ctrlin = newctrl;
|
||||
|
||||
dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
|
||||
acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
|
||||
acm->ctrlin = newctrl;
|
||||
|
||||
dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
|
||||
acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
|
||||
dr->bNotificationType, dr->wIndex,
|
||||
dr->wLength, data[0], data[1]);
|
||||
break;
|
||||
default:
|
||||
dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
|
||||
dr->bNotificationType, dr->wIndex,
|
||||
dr->wLength, data[0], data[1]);
|
||||
break;
|
||||
}
|
||||
exit:
|
||||
usb_mark_last_busy(acm->dev);
|
||||
retval = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
|
||||
"result %d", __func__, retval);
|
||||
@ -371,15 +383,14 @@ static void acm_rx_tasklet(unsigned long _acm)
|
||||
{
|
||||
struct acm *acm = (void *)_acm;
|
||||
struct acm_rb *buf;
|
||||
struct tty_struct *tty = acm->tty;
|
||||
struct tty_struct *tty;
|
||||
struct acm_ru *rcv;
|
||||
unsigned long flags;
|
||||
unsigned char throttled;
|
||||
|
||||
dbg("Entering acm_rx_tasklet");
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
{
|
||||
if (!ACM_READY(acm)) {
|
||||
dbg("acm_rx_tasklet: ACM not ready");
|
||||
return;
|
||||
}
|
||||
@ -387,12 +398,13 @@ static void acm_rx_tasklet(unsigned long _acm)
|
||||
spin_lock_irqsave(&acm->throttle_lock, flags);
|
||||
throttled = acm->throttle;
|
||||
spin_unlock_irqrestore(&acm->throttle_lock, flags);
|
||||
if (throttled)
|
||||
{
|
||||
if (throttled) {
|
||||
dbg("acm_rx_tasklet: throttled");
|
||||
return;
|
||||
}
|
||||
|
||||
tty = tty_port_tty_get(&acm->port);
|
||||
|
||||
next_buffer:
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
if (list_empty(&acm->filled_read_bufs)) {
|
||||
@ -406,20 +418,22 @@ next_buffer:
|
||||
|
||||
dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
|
||||
|
||||
tty_buffer_request_room(tty, buf->size);
|
||||
spin_lock_irqsave(&acm->throttle_lock, flags);
|
||||
throttled = acm->throttle;
|
||||
spin_unlock_irqrestore(&acm->throttle_lock, flags);
|
||||
if (!throttled)
|
||||
tty_insert_flip_string(tty, buf->base, buf->size);
|
||||
tty_flip_buffer_push(tty);
|
||||
|
||||
if (throttled) {
|
||||
dbg("Throttling noticed");
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
list_add(&buf->list, &acm->filled_read_bufs);
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
return;
|
||||
if (tty) {
|
||||
spin_lock_irqsave(&acm->throttle_lock, flags);
|
||||
throttled = acm->throttle;
|
||||
spin_unlock_irqrestore(&acm->throttle_lock, flags);
|
||||
if (!throttled) {
|
||||
tty_buffer_request_room(tty, buf->size);
|
||||
tty_insert_flip_string(tty, buf->base, buf->size);
|
||||
tty_flip_buffer_push(tty);
|
||||
} else {
|
||||
tty_kref_put(tty);
|
||||
dbg("Throttling noticed");
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
list_add(&buf->list, &acm->filled_read_bufs);
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
@ -428,6 +442,8 @@ next_buffer:
|
||||
goto next_buffer;
|
||||
|
||||
urbs:
|
||||
tty_kref_put(tty);
|
||||
|
||||
while (!list_empty(&acm->spare_read_bufs)) {
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
if (list_empty(&acm->spare_read_urbs)) {
|
||||
@ -454,10 +470,11 @@ urbs:
|
||||
rcv->urb->transfer_dma = buf->dma;
|
||||
rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
/* This shouldn't kill the driver as unsuccessful URBs are returned to the
|
||||
free-urbs-pool and resubmited ASAP */
|
||||
/* This shouldn't kill the driver as unsuccessful URBs are
|
||||
returned to the free-urbs-pool and resubmited ASAP */
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
|
||||
if (acm->susp_count ||
|
||||
usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
|
||||
list_add(&buf->list, &acm->spare_read_bufs);
|
||||
list_add(&rcv->list, &acm->spare_read_urbs);
|
||||
acm->processing = 0;
|
||||
@ -499,11 +516,14 @@ static void acm_write_bulk(struct urb *urb)
|
||||
static void acm_softint(struct work_struct *work)
|
||||
{
|
||||
struct acm *acm = container_of(work, struct acm, work);
|
||||
struct tty_struct *tty;
|
||||
|
||||
dev_vdbg(&acm->data->dev, "tx work\n");
|
||||
if (!ACM_READY(acm))
|
||||
return;
|
||||
tty_wakeup(acm->tty);
|
||||
tty = tty_port_tty_get(&acm->port);
|
||||
tty_wakeup(tty);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
static void acm_waker(struct work_struct *waker)
|
||||
@ -543,8 +563,9 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
|
||||
rv = 0;
|
||||
|
||||
set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
|
||||
|
||||
tty->driver_data = acm;
|
||||
acm->tty = tty;
|
||||
tty_port_tty_set(&acm->port, tty);
|
||||
|
||||
if (usb_autopm_get_interface(acm->control) < 0)
|
||||
goto early_bail;
|
||||
@ -552,11 +573,10 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
|
||||
acm->control->needs_remote_wakeup = 1;
|
||||
|
||||
mutex_lock(&acm->mutex);
|
||||
if (acm->used++) {
|
||||
if (acm->port.count++) {
|
||||
usb_autopm_put_interface(acm->control);
|
||||
goto done;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
acm->ctrlurb->dev = acm->dev;
|
||||
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
|
||||
@ -567,22 +587,22 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
|
||||
if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
|
||||
(acm->ctrl_caps & USB_CDC_CAP_LINE))
|
||||
goto full_bailout;
|
||||
|
||||
usb_autopm_put_interface(acm->control);
|
||||
|
||||
INIT_LIST_HEAD(&acm->spare_read_urbs);
|
||||
INIT_LIST_HEAD(&acm->spare_read_bufs);
|
||||
INIT_LIST_HEAD(&acm->filled_read_bufs);
|
||||
for (i = 0; i < acm->rx_buflimit; i++) {
|
||||
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
|
||||
}
|
||||
for (i = 0; i < acm->rx_buflimit; i++) {
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
|
||||
}
|
||||
|
||||
acm->throttle = 0;
|
||||
|
||||
tasklet_schedule(&acm->urb_task);
|
||||
|
||||
rv = tty_port_block_til_ready(&acm->port, tty, filp);
|
||||
done:
|
||||
mutex_unlock(&acm->mutex);
|
||||
err_out:
|
||||
@ -593,16 +613,17 @@ full_bailout:
|
||||
usb_kill_urb(acm->ctrlurb);
|
||||
bail_out:
|
||||
usb_autopm_put_interface(acm->control);
|
||||
acm->used--;
|
||||
acm->port.count--;
|
||||
mutex_unlock(&acm->mutex);
|
||||
early_bail:
|
||||
mutex_unlock(&open_mutex);
|
||||
tty_port_tty_set(&acm->port, NULL);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void acm_tty_unregister(struct acm *acm)
|
||||
{
|
||||
int i,nr;
|
||||
int i, nr;
|
||||
|
||||
nr = acm->rx_buflimit;
|
||||
tty_unregister_device(acm_tty_driver, acm->minor);
|
||||
@ -619,41 +640,56 @@ static void acm_tty_unregister(struct acm *acm)
|
||||
|
||||
static int acm_tty_chars_in_buffer(struct tty_struct *tty);
|
||||
|
||||
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
|
||||
static void acm_port_down(struct acm *acm, int drain)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
int i,nr;
|
||||
|
||||
if (!acm || !acm->used)
|
||||
return;
|
||||
|
||||
nr = acm->rx_buflimit;
|
||||
int i, nr = acm->rx_buflimit;
|
||||
mutex_lock(&open_mutex);
|
||||
if (!--acm->used) {
|
||||
if (acm->dev) {
|
||||
usb_autopm_get_interface(acm->control);
|
||||
acm_set_control(acm, acm->ctrlout = 0);
|
||||
|
||||
/* try letting the last writes drain naturally */
|
||||
if (acm->dev) {
|
||||
usb_autopm_get_interface(acm->control);
|
||||
acm_set_control(acm, acm->ctrlout = 0);
|
||||
/* try letting the last writes drain naturally */
|
||||
if (drain) {
|
||||
wait_event_interruptible_timeout(acm->drain_wait,
|
||||
(ACM_NW == acm_wb_is_avail(acm))
|
||||
|| !acm->dev,
|
||||
(ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
|
||||
ACM_CLOSE_TIMEOUT * HZ);
|
||||
|
||||
usb_kill_urb(acm->ctrlurb);
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_kill_urb(acm->wb[i].urb);
|
||||
for (i = 0; i < nr; i++)
|
||||
usb_kill_urb(acm->ru[i].urb);
|
||||
acm->control->needs_remote_wakeup = 0;
|
||||
usb_autopm_put_interface(acm->control);
|
||||
} else
|
||||
acm_tty_unregister(acm);
|
||||
}
|
||||
usb_kill_urb(acm->ctrlurb);
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_kill_urb(acm->wb[i].urb);
|
||||
for (i = 0; i < nr; i++)
|
||||
usb_kill_urb(acm->ru[i].urb);
|
||||
acm->control->needs_remote_wakeup = 0;
|
||||
usb_autopm_put_interface(acm->control);
|
||||
}
|
||||
mutex_unlock(&open_mutex);
|
||||
}
|
||||
|
||||
static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
||||
static void acm_tty_hangup(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
tty_port_hangup(&acm->port);
|
||||
acm_port_down(acm, 0);
|
||||
}
|
||||
|
||||
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
||||
/* Perform the closing process and see if we need to do the hardware
|
||||
shutdown */
|
||||
if (tty_port_close_start(&acm->port, tty, filp) == 0)
|
||||
return;
|
||||
acm_port_down(acm, 0);
|
||||
tty_port_close_end(&acm->port, tty);
|
||||
mutex_lock(&open_mutex);
|
||||
tty_port_tty_set(&acm->port, NULL);
|
||||
if (!acm->dev)
|
||||
acm_tty_unregister(acm);
|
||||
mutex_unlock(&open_mutex);
|
||||
}
|
||||
|
||||
static int acm_tty_write(struct tty_struct *tty,
|
||||
const unsigned char *buf, int count)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
int stat;
|
||||
@ -669,7 +705,8 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
if ((wbn = acm_wb_alloc(acm)) < 0) {
|
||||
wbn = acm_wb_alloc(acm);
|
||||
if (wbn < 0) {
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
@ -681,7 +718,8 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c
|
||||
wb->len = count;
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
|
||||
if ((stat = acm_write_start(acm, wbn)) < 0)
|
||||
stat = acm_write_start(acm, wbn);
|
||||
if (stat < 0)
|
||||
return stat;
|
||||
return count;
|
||||
}
|
||||
@ -767,8 +805,10 @@ static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
return -EINVAL;
|
||||
|
||||
newctrl = acm->ctrlout;
|
||||
set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
|
||||
clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
|
||||
set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
|
||||
(set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
|
||||
clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
|
||||
(clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
|
||||
|
||||
newctrl = (newctrl & ~clear) | set;
|
||||
|
||||
@ -777,7 +817,8 @@ static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
return acm_set_control(acm, acm->ctrlout = newctrl);
|
||||
}
|
||||
|
||||
static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
|
||||
static int acm_tty_ioctl(struct tty_struct *tty, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
||||
@ -799,7 +840,8 @@ static const __u8 acm_tty_size[] = {
|
||||
5, 6, 7, 8
|
||||
};
|
||||
|
||||
static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old)
|
||||
static void acm_tty_set_termios(struct tty_struct *tty,
|
||||
struct ktermios *termios_old)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
struct ktermios *termios = tty->termios;
|
||||
@ -809,19 +851,23 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios
|
||||
if (!ACM_READY(acm))
|
||||
return;
|
||||
|
||||
/* FIXME: Needs to support the tty_baud interface */
|
||||
/* FIXME: Broken on sparc */
|
||||
newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
|
||||
(termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
|
||||
newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
|
||||
newline.bParityType = termios->c_cflag & PARENB ?
|
||||
(termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
|
||||
(termios->c_cflag & PARODD ? 1 : 2) +
|
||||
(termios->c_cflag & CMSPAR ? 2 : 0) : 0;
|
||||
newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
|
||||
|
||||
/* FIXME: Needs to clear unsupported bits in the termios */
|
||||
acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
|
||||
|
||||
if (!newline.dwDTERate) {
|
||||
newline.dwDTERate = acm->line.dwDTERate;
|
||||
newctrl &= ~ACM_CTRL_DTR;
|
||||
} else newctrl |= ACM_CTRL_DTR;
|
||||
} else
|
||||
newctrl |= ACM_CTRL_DTR;
|
||||
|
||||
if (newctrl != acm->ctrlout)
|
||||
acm_set_control(acm, acm->ctrlout = newctrl);
|
||||
@ -846,9 +892,8 @@ static void acm_write_buffers_free(struct acm *acm)
|
||||
struct acm_wb *wb;
|
||||
struct usb_device *usb_dev = interface_to_usbdev(acm->control);
|
||||
|
||||
for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
|
||||
for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
|
||||
usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);
|
||||
}
|
||||
}
|
||||
|
||||
static void acm_read_buffers_free(struct acm *acm)
|
||||
@ -857,7 +902,8 @@ static void acm_read_buffers_free(struct acm *acm)
|
||||
int i, n = acm->rx_buflimit;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
|
||||
usb_buffer_free(usb_dev, acm->readsize,
|
||||
acm->rb[i].base, acm->rb[i].dma);
|
||||
}
|
||||
|
||||
/* Little helper: write buffers allocate */
|
||||
@ -882,8 +928,8 @@ static int acm_write_buffers_alloc(struct acm *acm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acm_probe (struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
static int acm_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_cdc_union_desc *union_header = NULL;
|
||||
struct usb_cdc_country_functional_desc *cfd = NULL;
|
||||
@ -897,7 +943,7 @@ static int acm_probe (struct usb_interface *intf,
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
struct acm *acm;
|
||||
int minor;
|
||||
int ctrlsize,readsize;
|
||||
int ctrlsize, readsize;
|
||||
u8 *buf;
|
||||
u8 ac_management_function = 0;
|
||||
u8 call_management_function = 0;
|
||||
@ -917,7 +963,7 @@ static int acm_probe (struct usb_interface *intf,
|
||||
control_interface = usb_ifnum_to_if(usb_dev, 0);
|
||||
goto skip_normal_probe;
|
||||
}
|
||||
|
||||
|
||||
/* normal probing*/
|
||||
if (!buffer) {
|
||||
dev_err(&intf->dev, "Weird descriptor references\n");
|
||||
@ -925,8 +971,10 @@ static int acm_probe (struct usb_interface *intf,
|
||||
}
|
||||
|
||||
if (!buflen) {
|
||||
if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
|
||||
dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
|
||||
if (intf->cur_altsetting->endpoint->extralen &&
|
||||
intf->cur_altsetting->endpoint->extra) {
|
||||
dev_dbg(&intf->dev,
|
||||
"Seeking extra descriptors on endpoint\n");
|
||||
buflen = intf->cur_altsetting->endpoint->extralen;
|
||||
buffer = intf->cur_altsetting->endpoint->extra;
|
||||
} else {
|
||||
@ -937,47 +985,43 @@ static int acm_probe (struct usb_interface *intf,
|
||||
}
|
||||
|
||||
while (buflen > 0) {
|
||||
if (buffer [1] != USB_DT_CS_INTERFACE) {
|
||||
if (buffer[1] != USB_DT_CS_INTERFACE) {
|
||||
dev_err(&intf->dev, "skipping garbage\n");
|
||||
goto next_desc;
|
||||
}
|
||||
|
||||
switch (buffer [2]) {
|
||||
case USB_CDC_UNION_TYPE: /* we've found it */
|
||||
if (union_header) {
|
||||
dev_err(&intf->dev, "More than one "
|
||||
"union descriptor, "
|
||||
"skipping ...\n");
|
||||
goto next_desc;
|
||||
}
|
||||
union_header = (struct usb_cdc_union_desc *)
|
||||
buffer;
|
||||
break;
|
||||
case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
|
||||
cfd = (struct usb_cdc_country_functional_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_HEADER_TYPE: /* maybe check version */
|
||||
break; /* for now we ignore it */
|
||||
case USB_CDC_ACM_TYPE:
|
||||
ac_management_function = buffer[3];
|
||||
break;
|
||||
case USB_CDC_CALL_MANAGEMENT_TYPE:
|
||||
call_management_function = buffer[3];
|
||||
call_interface_num = buffer[4];
|
||||
if ((call_management_function & 3) != 3)
|
||||
dev_err(&intf->dev, "This device "
|
||||
"cannot do calls on its own. "
|
||||
"It is no modem.\n");
|
||||
break;
|
||||
default:
|
||||
/* there are LOTS more CDC descriptors that
|
||||
* could legitimately be found here.
|
||||
*/
|
||||
dev_dbg(&intf->dev, "Ignoring descriptor: "
|
||||
"type %02x, length %d\n",
|
||||
buffer[2], buffer[0]);
|
||||
break;
|
||||
switch (buffer[2]) {
|
||||
case USB_CDC_UNION_TYPE: /* we've found it */
|
||||
if (union_header) {
|
||||
dev_err(&intf->dev, "More than one "
|
||||
"union descriptor, skipping ...\n");
|
||||
goto next_desc;
|
||||
}
|
||||
union_header = (struct usb_cdc_union_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
|
||||
cfd = (struct usb_cdc_country_functional_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_HEADER_TYPE: /* maybe check version */
|
||||
break; /* for now we ignore it */
|
||||
case USB_CDC_ACM_TYPE:
|
||||
ac_management_function = buffer[3];
|
||||
break;
|
||||
case USB_CDC_CALL_MANAGEMENT_TYPE:
|
||||
call_management_function = buffer[3];
|
||||
call_interface_num = buffer[4];
|
||||
if ((call_management_function & 3) != 3)
|
||||
dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
|
||||
break;
|
||||
default:
|
||||
/* there are LOTS more CDC descriptors that
|
||||
* could legitimately be found here.
|
||||
*/
|
||||
dev_dbg(&intf->dev, "Ignoring descriptor: "
|
||||
"type %02x, length %d\n",
|
||||
buffer[2], buffer[0]);
|
||||
break;
|
||||
}
|
||||
next_desc:
|
||||
buflen -= buffer[0];
|
||||
buffer += buffer[0];
|
||||
@ -985,33 +1029,36 @@ next_desc:
|
||||
|
||||
if (!union_header) {
|
||||
if (call_interface_num > 0) {
|
||||
dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
|
||||
dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
|
||||
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
|
||||
control_interface = intf;
|
||||
} else {
|
||||
dev_dbg(&intf->dev,"No union descriptor, giving up\n");
|
||||
dev_dbg(&intf->dev,
|
||||
"No union descriptor, giving up\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
} else {
|
||||
control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
|
||||
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
|
||||
if (!control_interface || !data_interface) {
|
||||
dev_dbg(&intf->dev,"no interfaces\n");
|
||||
dev_dbg(&intf->dev, "no interfaces\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (data_interface_num != call_interface_num)
|
||||
dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n");
|
||||
dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
|
||||
|
||||
skip_normal_probe:
|
||||
|
||||
/*workaround for switched interfaces */
|
||||
if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
|
||||
if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
|
||||
if (data_interface->cur_altsetting->desc.bInterfaceClass
|
||||
!= CDC_DATA_INTERFACE_TYPE) {
|
||||
if (control_interface->cur_altsetting->desc.bInterfaceClass
|
||||
== CDC_DATA_INTERFACE_TYPE) {
|
||||
struct usb_interface *t;
|
||||
dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
|
||||
|
||||
dev_dbg(&intf->dev,
|
||||
"Your device has switched interfaces.\n");
|
||||
t = control_interface;
|
||||
control_interface = data_interface;
|
||||
data_interface = t;
|
||||
@ -1023,9 +1070,9 @@ skip_normal_probe:
|
||||
/* Accept probe requests only for the control interface */
|
||||
if (intf != control_interface)
|
||||
return -ENODEV;
|
||||
|
||||
|
||||
if (usb_interface_claimed(data_interface)) { /* valid in this context */
|
||||
dev_dbg(&intf->dev,"The data interface isn't available\n");
|
||||
dev_dbg(&intf->dev, "The data interface isn't available\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
@ -1042,8 +1089,8 @@ skip_normal_probe:
|
||||
if (!usb_endpoint_dir_in(epread)) {
|
||||
/* descriptors are swapped */
|
||||
struct usb_endpoint_descriptor *t;
|
||||
dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
|
||||
|
||||
dev_dbg(&intf->dev,
|
||||
"The data interface has switched endpoints\n");
|
||||
t = epread;
|
||||
epread = epwrite;
|
||||
epwrite = t;
|
||||
@ -1056,13 +1103,15 @@ skip_normal_probe:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
|
||||
acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
|
||||
if (acm == NULL) {
|
||||
dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
|
||||
goto alloc_fail;
|
||||
}
|
||||
|
||||
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
|
||||
readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
|
||||
readsize = le16_to_cpu(epread->wMaxPacketSize) *
|
||||
(quirks == SINGLE_RX_URB ? 1 : 2);
|
||||
acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
|
||||
acm->control = control_interface;
|
||||
acm->data = data_interface;
|
||||
@ -1082,6 +1131,8 @@ skip_normal_probe:
|
||||
spin_lock_init(&acm->read_lock);
|
||||
mutex_init(&acm->mutex);
|
||||
acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
|
||||
tty_port_init(&acm->port);
|
||||
acm->port.ops = &acm_port_ops;
|
||||
|
||||
buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
|
||||
if (!buf) {
|
||||
@ -1103,8 +1154,10 @@ skip_normal_probe:
|
||||
for (i = 0; i < num_rx_buf; i++) {
|
||||
struct acm_ru *rcv = &(acm->ru[i]);
|
||||
|
||||
if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
|
||||
dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
|
||||
rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (rcv->urb == NULL) {
|
||||
dev_dbg(&intf->dev,
|
||||
"out of memory (read urbs usb_alloc_urb)\n");
|
||||
goto alloc_fail7;
|
||||
}
|
||||
|
||||
@ -1117,26 +1170,29 @@ skip_normal_probe:
|
||||
rb->base = usb_buffer_alloc(acm->dev, readsize,
|
||||
GFP_KERNEL, &rb->dma);
|
||||
if (!rb->base) {
|
||||
dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
|
||||
dev_dbg(&intf->dev,
|
||||
"out of memory (read bufs usb_buffer_alloc)\n");
|
||||
goto alloc_fail7;
|
||||
}
|
||||
}
|
||||
for(i = 0; i < ACM_NW; i++)
|
||||
{
|
||||
for (i = 0; i < ACM_NW; i++) {
|
||||
struct acm_wb *snd = &(acm->wb[i]);
|
||||
|
||||
if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
|
||||
dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)");
|
||||
snd->urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (snd->urb == NULL) {
|
||||
dev_dbg(&intf->dev,
|
||||
"out of memory (write urbs usb_alloc_urb)");
|
||||
goto alloc_fail7;
|
||||
}
|
||||
|
||||
usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
|
||||
NULL, acm->writesize, acm_write_bulk, snd);
|
||||
usb_fill_bulk_urb(snd->urb, usb_dev,
|
||||
usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
|
||||
NULL, acm->writesize, acm_write_bulk, snd);
|
||||
snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
snd->instance = acm;
|
||||
}
|
||||
|
||||
usb_set_intfdata (intf, acm);
|
||||
usb_set_intfdata(intf, acm);
|
||||
|
||||
i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
|
||||
if (i < 0)
|
||||
@ -1147,7 +1203,8 @@ skip_normal_probe:
|
||||
if (!acm->country_codes)
|
||||
goto skip_countries;
|
||||
acm->country_code_size = cfd->bLength - 4;
|
||||
memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
|
||||
memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
|
||||
cfd->bLength - 4);
|
||||
acm->country_rel_date = cfd->iCountryCodeRelDate;
|
||||
|
||||
i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
|
||||
@ -1156,7 +1213,8 @@ skip_normal_probe:
|
||||
goto skip_countries;
|
||||
}
|
||||
|
||||
i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
|
||||
i = device_create_file(&intf->dev,
|
||||
&dev_attr_iCountryCodeRelDate);
|
||||
if (i < 0) {
|
||||
kfree(acm->country_codes);
|
||||
goto skip_countries;
|
||||
@ -1164,8 +1222,10 @@ skip_normal_probe:
|
||||
}
|
||||
|
||||
skip_countries:
|
||||
usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
|
||||
acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
|
||||
usb_fill_int_urb(acm->ctrlurb, usb_dev,
|
||||
usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
|
||||
acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
|
||||
epctrl->bInterval);
|
||||
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
acm->ctrlurb->transfer_dma = acm->ctrl_dma;
|
||||
|
||||
@ -1212,7 +1272,7 @@ static void stop_data_traffic(struct acm *acm)
|
||||
tasklet_disable(&acm->urb_task);
|
||||
|
||||
usb_kill_urb(acm->ctrlurb);
|
||||
for(i = 0; i < ACM_NW; i++)
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_kill_urb(acm->wb[i].urb);
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
usb_kill_urb(acm->ru[i].urb);
|
||||
@ -1227,13 +1287,14 @@ static void acm_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct acm *acm = usb_get_intfdata(intf);
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
struct tty_struct *tty;
|
||||
|
||||
/* sibling interface is already cleaning up */
|
||||
if (!acm)
|
||||
return;
|
||||
|
||||
mutex_lock(&open_mutex);
|
||||
if (acm->country_codes){
|
||||
if (acm->country_codes) {
|
||||
device_remove_file(&acm->control->dev,
|
||||
&dev_attr_wCountryCodes);
|
||||
device_remove_file(&acm->control->dev,
|
||||
@ -1247,22 +1308,25 @@ static void acm_disconnect(struct usb_interface *intf)
|
||||
stop_data_traffic(acm);
|
||||
|
||||
acm_write_buffers_free(acm);
|
||||
usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
|
||||
usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
|
||||
acm->ctrl_dma);
|
||||
acm_read_buffers_free(acm);
|
||||
|
||||
usb_driver_release_interface(&acm_driver, intf == acm->control ?
|
||||
acm->data : acm->control);
|
||||
|
||||
if (!acm->used) {
|
||||
if (acm->port.count == 0) {
|
||||
acm_tty_unregister(acm);
|
||||
mutex_unlock(&open_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_unlock(&open_mutex);
|
||||
|
||||
if (acm->tty)
|
||||
tty_hangup(acm->tty);
|
||||
tty = tty_port_tty_get(&acm->port);
|
||||
if (tty) {
|
||||
tty_hangup(tty);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -1297,7 +1361,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
*/
|
||||
mutex_lock(&acm->mutex);
|
||||
|
||||
if (acm->used)
|
||||
if (acm->port.count)
|
||||
stop_data_traffic(acm);
|
||||
|
||||
mutex_unlock(&acm->mutex);
|
||||
@ -1319,7 +1383,7 @@ static int acm_resume(struct usb_interface *intf)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&acm->mutex);
|
||||
if (acm->used) {
|
||||
if (acm->port.count) {
|
||||
rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
|
||||
if (rv < 0)
|
||||
goto err_out;
|
||||
@ -1398,7 +1462,7 @@ static struct usb_device_id acm_ids[] = {
|
||||
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
|
||||
USB_CDC_ACM_PROTO_AT_GSM) },
|
||||
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
|
||||
USB_CDC_ACM_PROTO_AT_3G ) },
|
||||
USB_CDC_ACM_PROTO_AT_3G) },
|
||||
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
|
||||
USB_CDC_ACM_PROTO_AT_CDMA) },
|
||||
|
||||
@ -1406,7 +1470,7 @@ static struct usb_device_id acm_ids[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, acm_ids);
|
||||
MODULE_DEVICE_TABLE(usb, acm_ids);
|
||||
|
||||
static struct usb_driver acm_driver = {
|
||||
.name = "cdc_acm",
|
||||
@ -1429,6 +1493,7 @@ static struct usb_driver acm_driver = {
|
||||
static const struct tty_operations acm_ops = {
|
||||
.open = acm_tty_open,
|
||||
.close = acm_tty_close,
|
||||
.hangup = acm_tty_hangup,
|
||||
.write = acm_tty_write,
|
||||
.write_room = acm_tty_write_room,
|
||||
.ioctl = acm_tty_ioctl,
|
||||
@ -1460,7 +1525,8 @@ static int __init acm_init(void)
|
||||
acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
|
||||
acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
||||
acm_tty_driver->init_termios = tty_std_termios;
|
||||
acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
||||
acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
|
||||
HUPCL | CLOCAL;
|
||||
tty_set_operations(acm_tty_driver, &acm_ops);
|
||||
|
||||
retval = tty_register_driver(acm_tty_driver);
|
||||
@ -1492,7 +1558,7 @@ static void __exit acm_exit(void)
|
||||
module_init(acm_init);
|
||||
module_exit(acm_exit);
|
||||
|
||||
MODULE_AUTHOR( DRIVER_AUTHOR );
|
||||
MODULE_DESCRIPTION( DRIVER_DESC );
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);
|
||||
|
@ -89,8 +89,8 @@ struct acm {
|
||||
struct usb_device *dev; /* the corresponding usb device */
|
||||
struct usb_interface *control; /* control interface */
|
||||
struct usb_interface *data; /* data interface */
|
||||
struct tty_struct *tty; /* the corresponding tty */
|
||||
struct urb *ctrlurb; /* urbs */
|
||||
struct tty_port port; /* our tty port data */
|
||||
struct urb *ctrlurb; /* urbs */
|
||||
u8 *ctrl_buffer; /* buffers of urbs */
|
||||
dma_addr_t ctrl_dma; /* dma handles of buffers */
|
||||
u8 *country_codes; /* country codes from device */
|
||||
@ -120,7 +120,6 @@ struct acm {
|
||||
unsigned int ctrlout; /* output control lines (DTR, RTS) */
|
||||
unsigned int writesize; /* max packet size for the output bulk endpoint */
|
||||
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
|
||||
unsigned int used; /* someone has this acm's device open */
|
||||
unsigned int minor; /* acm minor number */
|
||||
unsigned char throttle; /* throttled by tty layer */
|
||||
unsigned char clocal; /* termios CLOCAL */
|
||||
|
@ -93,8 +93,7 @@ static int belkin_sa_startup(struct usb_serial *serial);
|
||||
static void belkin_sa_shutdown(struct usb_serial *serial);
|
||||
static int belkin_sa_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void belkin_sa_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void belkin_sa_close(struct usb_serial_port *port);
|
||||
static void belkin_sa_read_int_callback(struct urb *urb);
|
||||
static void belkin_sa_set_termios(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct ktermios * old);
|
||||
@ -244,8 +243,7 @@ exit:
|
||||
} /* belkin_sa_open */
|
||||
|
||||
|
||||
static void belkin_sa_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void belkin_sa_close(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s port %d", __func__, port->number);
|
||||
|
||||
|
@ -262,13 +262,33 @@ error: kfree(priv);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void ch341_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp)
|
||||
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;
|
||||
unsigned int c_cflag;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
/* 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);
|
||||
wake_up_interruptible(&priv->delta_msr_wait);
|
||||
}
|
||||
|
||||
static void ch341_close(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
/* shutdown our urbs */
|
||||
@ -276,18 +296,6 @@ static void ch341_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
usb_kill_urb(port->write_urb);
|
||||
usb_kill_urb(port->read_urb);
|
||||
usb_kill_urb(port->interrupt_in_urb);
|
||||
|
||||
if (tty) {
|
||||
c_cflag = tty->termios->c_cflag;
|
||||
if (c_cflag & HUPCL) {
|
||||
/* drop DTR and RTS */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->line_control = 0;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
ch341_set_handshake(port->serial->dev, 0);
|
||||
}
|
||||
}
|
||||
wake_up_interruptible(&priv->delta_msr_wait);
|
||||
}
|
||||
|
||||
|
||||
@ -302,7 +310,6 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
dbg("ch341_open()");
|
||||
|
||||
priv->baud_rate = DEFAULT_BAUD_RATE;
|
||||
priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
|
||||
|
||||
r = ch341_configure(serial->dev, priv);
|
||||
if (r)
|
||||
@ -322,7 +329,7 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
if (r) {
|
||||
dev_err(&port->dev, "%s - failed submitting interrupt urb,"
|
||||
" error %d\n", __func__, r);
|
||||
ch341_close(tty, port, NULL);
|
||||
ch341_close(port);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
@ -343,9 +350,6 @@ static void ch341_set_termios(struct tty_struct *tty,
|
||||
|
||||
dbg("ch341_set_termios()");
|
||||
|
||||
if (!tty || !tty->termios)
|
||||
return;
|
||||
|
||||
baud_rate = tty_get_baud_rate(tty);
|
||||
|
||||
priv->baud_rate = baud_rate;
|
||||
@ -568,6 +572,8 @@ static struct usb_serial_driver ch341_device = {
|
||||
.usb_driver = &ch341_driver,
|
||||
.num_ports = 1,
|
||||
.open = ch341_open,
|
||||
.dtr_rts = ch341_dtr_rts,
|
||||
.carrier_raised = ch341_carrier_raised,
|
||||
.close = ch341_close,
|
||||
.ioctl = ch341_ioctl,
|
||||
.set_termios = ch341_set_termios,
|
||||
|
@ -169,7 +169,9 @@ static int usb_console_setup(struct console *co, char *options)
|
||||
kfree(tty);
|
||||
}
|
||||
}
|
||||
|
||||
/* So we know not to kill the hardware on a hangup on this
|
||||
port. We have also bumped the use count by one so it won't go
|
||||
idle */
|
||||
port->console = 1;
|
||||
retval = 0;
|
||||
|
||||
@ -182,7 +184,7 @@ free_tty:
|
||||
kfree(tty);
|
||||
reset_open_count:
|
||||
port->port.count = 0;
|
||||
goto out;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void usb_console_write(struct console *co,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Silicon Laboratories CP2101/CP2102 USB to RS232 serial adaptor driver
|
||||
* Silicon Laboratories CP210x USB to RS232 serial adaptor driver
|
||||
*
|
||||
* Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk)
|
||||
*
|
||||
@ -27,44 +27,46 @@
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v0.08"
|
||||
#define DRIVER_DESC "Silicon Labs CP2101/CP2102 RS232 serial adaptor driver"
|
||||
#define DRIVER_VERSION "v0.09"
|
||||
#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"
|
||||
|
||||
/*
|
||||
* Function Prototypes
|
||||
*/
|
||||
static int cp2101_open(struct tty_struct *, struct usb_serial_port *,
|
||||
static int cp210x_open(struct tty_struct *, struct usb_serial_port *,
|
||||
struct file *);
|
||||
static void cp2101_cleanup(struct usb_serial_port *);
|
||||
static void cp2101_close(struct tty_struct *, struct usb_serial_port *,
|
||||
struct file*);
|
||||
static void cp2101_get_termios(struct tty_struct *,
|
||||
static void cp210x_cleanup(struct usb_serial_port *);
|
||||
static void cp210x_close(struct usb_serial_port *);
|
||||
static void cp210x_get_termios(struct tty_struct *,
|
||||
struct usb_serial_port *port);
|
||||
static void cp2101_get_termios_port(struct usb_serial_port *port,
|
||||
static void cp210x_get_termios_port(struct usb_serial_port *port,
|
||||
unsigned int *cflagp, unsigned int *baudp);
|
||||
static void cp2101_set_termios(struct tty_struct *, struct usb_serial_port *,
|
||||
static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *,
|
||||
struct ktermios*);
|
||||
static int cp2101_tiocmget(struct tty_struct *, struct file *);
|
||||
static int cp2101_tiocmset(struct tty_struct *, struct file *,
|
||||
static int cp210x_tiocmget(struct tty_struct *, struct file *);
|
||||
static int cp210x_tiocmset(struct tty_struct *, struct file *,
|
||||
unsigned int, unsigned int);
|
||||
static int cp2101_tiocmset_port(struct usb_serial_port *port, struct file *,
|
||||
static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *,
|
||||
unsigned int, unsigned int);
|
||||
static void cp2101_break_ctl(struct tty_struct *, int);
|
||||
static int cp2101_startup(struct usb_serial *);
|
||||
static void cp2101_shutdown(struct usb_serial *);
|
||||
static void cp210x_break_ctl(struct tty_struct *, int);
|
||||
static int cp210x_startup(struct usb_serial *);
|
||||
static void cp210x_shutdown(struct usb_serial *);
|
||||
|
||||
static int debug;
|
||||
|
||||
static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */
|
||||
{ USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
|
||||
{ USB_DEVICE(0x0745, 0x1000) }, /* CipherLab USB CCD Barcode Scanner 1000 */
|
||||
{ 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(0x0FCF, 0x1003) }, /* Dynastream ANT development board */
|
||||
{ USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */
|
||||
{ USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */
|
||||
{ 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, 0x800A) }, /* SPORTident BSM7-D-USB main station */
|
||||
{ USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
|
||||
{ USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */
|
||||
@ -85,10 +87,12 @@ static struct usb_device_id id_table [] = {
|
||||
{ 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, 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 demostration module */
|
||||
{ USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */
|
||||
{ USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */
|
||||
{ USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */
|
||||
{ USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */
|
||||
{ USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */
|
||||
@ -99,7 +103,9 @@ static struct usb_device_id id_table [] = {
|
||||
{ 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, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */
|
||||
{ USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
|
||||
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
|
||||
@ -108,53 +114,70 @@ static struct usb_device_id id_table [] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
static struct usb_driver cp2101_driver = {
|
||||
.name = "cp2101",
|
||||
static struct usb_driver cp210x_driver = {
|
||||
.name = "cp210x",
|
||||
.probe = usb_serial_probe,
|
||||
.disconnect = usb_serial_disconnect,
|
||||
.id_table = id_table,
|
||||
.no_dynamic_id = 1,
|
||||
};
|
||||
|
||||
static struct usb_serial_driver cp2101_device = {
|
||||
static struct usb_serial_driver cp210x_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cp2101",
|
||||
.name = "cp210x",
|
||||
},
|
||||
.usb_driver = &cp2101_driver,
|
||||
.usb_driver = &cp210x_driver,
|
||||
.id_table = id_table,
|
||||
.num_ports = 1,
|
||||
.open = cp2101_open,
|
||||
.close = cp2101_close,
|
||||
.break_ctl = cp2101_break_ctl,
|
||||
.set_termios = cp2101_set_termios,
|
||||
.tiocmget = cp2101_tiocmget,
|
||||
.tiocmset = cp2101_tiocmset,
|
||||
.attach = cp2101_startup,
|
||||
.shutdown = cp2101_shutdown,
|
||||
.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,
|
||||
.shutdown = cp210x_shutdown,
|
||||
};
|
||||
|
||||
/* Config request types */
|
||||
#define REQTYPE_HOST_TO_DEVICE 0x41
|
||||
#define REQTYPE_DEVICE_TO_HOST 0xc1
|
||||
|
||||
/* Config SET requests. To GET, add 1 to the request number */
|
||||
#define CP2101_UART 0x00 /* Enable / Disable */
|
||||
#define CP2101_BAUDRATE 0x01 /* (BAUD_RATE_GEN_FREQ / baudrate) */
|
||||
#define CP2101_BITS 0x03 /* 0x(0)(databits)(parity)(stopbits) */
|
||||
#define CP2101_BREAK 0x05 /* On / Off */
|
||||
#define CP2101_CONTROL 0x07 /* Flow control line states */
|
||||
#define CP2101_MODEMCTL 0x13 /* Modem controls */
|
||||
#define CP2101_CONFIG_6 0x19 /* 6 bytes of config data ??? */
|
||||
/* 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
|
||||
|
||||
/* CP2101_UART */
|
||||
/* CP210X_IFC_ENABLE */
|
||||
#define UART_ENABLE 0x0001
|
||||
#define UART_DISABLE 0x0000
|
||||
|
||||
/* CP2101_BAUDRATE */
|
||||
/* CP210X_(SET|GET)_BAUDDIV */
|
||||
#define BAUD_RATE_GEN_FREQ 0x384000
|
||||
|
||||
/* CP2101_BITS */
|
||||
/* CP210X_(SET|GET)_LINE_CTL */
|
||||
#define BITS_DATA_MASK 0X0f00
|
||||
#define BITS_DATA_5 0X0500
|
||||
#define BITS_DATA_6 0X0600
|
||||
@ -174,11 +197,11 @@ static struct usb_serial_driver cp2101_device = {
|
||||
#define BITS_STOP_1_5 0x0001
|
||||
#define BITS_STOP_2 0x0002
|
||||
|
||||
/* CP2101_BREAK */
|
||||
/* CP210X_SET_BREAK */
|
||||
#define BREAK_ON 0x0000
|
||||
#define BREAK_OFF 0x0001
|
||||
|
||||
/* CP2101_CONTROL */
|
||||
/* CP210X_(SET_MHS|GET_MDMSTS) */
|
||||
#define CONTROL_DTR 0x0001
|
||||
#define CONTROL_RTS 0x0002
|
||||
#define CONTROL_CTS 0x0010
|
||||
@ -189,13 +212,13 @@ static struct usb_serial_driver cp2101_device = {
|
||||
#define CONTROL_WRITE_RTS 0x0200
|
||||
|
||||
/*
|
||||
* cp2101_get_config
|
||||
* Reads from the CP2101 configuration registers
|
||||
* 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 cp2101_get_config(struct usb_serial_port *port, u8 request,
|
||||
static int cp210x_get_config(struct usb_serial_port *port, u8 request,
|
||||
unsigned int *data, int size)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
@ -211,9 +234,6 @@ static int cp2101_get_config(struct usb_serial_port *port, u8 request,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* For get requests, the request number must be incremented */
|
||||
request++;
|
||||
|
||||
/* Issue the request, attempting to read 'size' bytes */
|
||||
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
|
||||
request, REQTYPE_DEVICE_TO_HOST, 0x0000,
|
||||
@ -236,12 +256,12 @@ static int cp2101_get_config(struct usb_serial_port *port, u8 request,
|
||||
}
|
||||
|
||||
/*
|
||||
* cp2101_set_config
|
||||
* Writes to the CP2101 configuration registers
|
||||
* 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 cp2101_set_config(struct usb_serial_port *port, u8 request,
|
||||
static int cp210x_set_config(struct usb_serial_port *port, u8 request,
|
||||
unsigned int *data, int size)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
@ -292,21 +312,21 @@ static int cp2101_set_config(struct usb_serial_port *port, u8 request,
|
||||
}
|
||||
|
||||
/*
|
||||
* cp2101_set_config_single
|
||||
* Convenience function for calling cp2101_set_config on single data values
|
||||
* cp210x_set_config_single
|
||||
* Convenience function for calling cp210x_set_config on single data values
|
||||
* without requiring an integer pointer
|
||||
*/
|
||||
static inline int cp2101_set_config_single(struct usb_serial_port *port,
|
||||
static inline int cp210x_set_config_single(struct usb_serial_port *port,
|
||||
u8 request, unsigned int data)
|
||||
{
|
||||
return cp2101_set_config(port, request, &data, 2);
|
||||
return cp210x_set_config(port, request, &data, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* cp2101_quantise_baudrate
|
||||
* cp210x_quantise_baudrate
|
||||
* Quantises the baud rate as per AN205 Table 1
|
||||
*/
|
||||
static unsigned int cp2101_quantise_baudrate(unsigned int baud) {
|
||||
static unsigned int cp210x_quantise_baudrate(unsigned int baud) {
|
||||
if (baud <= 56) baud = 0;
|
||||
else if (baud <= 300) baud = 300;
|
||||
else if (baud <= 600) baud = 600;
|
||||
@ -343,7 +363,7 @@ static unsigned int cp2101_quantise_baudrate(unsigned int baud) {
|
||||
return baud;
|
||||
}
|
||||
|
||||
static int cp2101_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
@ -351,7 +371,7 @@ static int cp2101_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
if (cp2101_set_config_single(port, CP2101_UART, UART_ENABLE)) {
|
||||
if (cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_ENABLE)) {
|
||||
dev_err(&port->dev, "%s - Unable to enable UART\n",
|
||||
__func__);
|
||||
return -EPROTO;
|
||||
@ -373,17 +393,17 @@ static int cp2101_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
}
|
||||
|
||||
/* Configure the termios structure */
|
||||
cp2101_get_termios(tty, port);
|
||||
cp210x_get_termios(tty, port);
|
||||
|
||||
/* Set the DTR and RTS pins low */
|
||||
cp2101_tiocmset_port(tty ? (struct usb_serial_port *) tty->driver_data
|
||||
cp210x_tiocmset_port(tty ? (struct usb_serial_port *) tty->driver_data
|
||||
: port,
|
||||
NULL, TIOCM_DTR | TIOCM_RTS, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cp2101_cleanup(struct usb_serial_port *port)
|
||||
static void cp210x_cleanup(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
|
||||
@ -398,8 +418,7 @@ static void cp2101_cleanup(struct usb_serial_port *port)
|
||||
}
|
||||
}
|
||||
|
||||
static void cp2101_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp)
|
||||
static void cp210x_close(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
@ -410,23 +429,23 @@ static void cp2101_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
|
||||
mutex_lock(&port->serial->disc_mutex);
|
||||
if (!port->serial->disconnected)
|
||||
cp2101_set_config_single(port, CP2101_UART, UART_DISABLE);
|
||||
cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE);
|
||||
mutex_unlock(&port->serial->disc_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* cp2101_get_termios
|
||||
* 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 cp2101_get_termios(struct tty_struct *tty,
|
||||
static void cp210x_get_termios(struct tty_struct *tty,
|
||||
struct usb_serial_port *port)
|
||||
{
|
||||
unsigned int baud;
|
||||
|
||||
if (tty) {
|
||||
cp2101_get_termios_port(tty->driver_data,
|
||||
cp210x_get_termios_port(tty->driver_data,
|
||||
&tty->termios->c_cflag, &baud);
|
||||
tty_encode_baud_rate(tty, baud, baud);
|
||||
}
|
||||
@ -434,15 +453,15 @@ static void cp2101_get_termios(struct tty_struct *tty,
|
||||
else {
|
||||
unsigned int cflag;
|
||||
cflag = 0;
|
||||
cp2101_get_termios_port(port, &cflag, &baud);
|
||||
cp210x_get_termios_port(port, &cflag, &baud);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* cp2101_get_termios_port
|
||||
* This is the heart of cp2101_get_termios which always uses a &usb_serial_port.
|
||||
* cp210x_get_termios_port
|
||||
* This is the heart of cp210x_get_termios which always uses a &usb_serial_port.
|
||||
*/
|
||||
static void cp2101_get_termios_port(struct usb_serial_port *port,
|
||||
static void cp210x_get_termios_port(struct usb_serial_port *port,
|
||||
unsigned int *cflagp, unsigned int *baudp)
|
||||
{
|
||||
unsigned int cflag, modem_ctl[4];
|
||||
@ -451,17 +470,17 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
cp2101_get_config(port, CP2101_BAUDRATE, &baud, 2);
|
||||
cp210x_get_config(port, CP210X_GET_BAUDDIV, &baud, 2);
|
||||
/* Convert to baudrate */
|
||||
if (baud)
|
||||
baud = cp2101_quantise_baudrate((BAUD_RATE_GEN_FREQ + baud/2)/ baud);
|
||||
baud = cp210x_quantise_baudrate((BAUD_RATE_GEN_FREQ + baud/2)/ baud);
|
||||
|
||||
dbg("%s - baud rate = %d", __func__, baud);
|
||||
*baudp = baud;
|
||||
|
||||
cflag = *cflagp;
|
||||
|
||||
cp2101_get_config(port, CP2101_BITS, &bits, 2);
|
||||
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
|
||||
cflag &= ~CSIZE;
|
||||
switch (bits & BITS_DATA_MASK) {
|
||||
case BITS_DATA_5:
|
||||
@ -486,14 +505,14 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
|
||||
cflag |= CS8;
|
||||
bits &= ~BITS_DATA_MASK;
|
||||
bits |= BITS_DATA_8;
|
||||
cp2101_set_config(port, CP2101_BITS, &bits, 2);
|
||||
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
|
||||
break;
|
||||
default:
|
||||
dbg("%s - Unknown number of data bits, using 8", __func__);
|
||||
cflag |= CS8;
|
||||
bits &= ~BITS_DATA_MASK;
|
||||
bits |= BITS_DATA_8;
|
||||
cp2101_set_config(port, CP2101_BITS, &bits, 2);
|
||||
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -516,20 +535,20 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
|
||||
__func__);
|
||||
cflag &= ~PARENB;
|
||||
bits &= ~BITS_PARITY_MASK;
|
||||
cp2101_set_config(port, CP2101_BITS, &bits, 2);
|
||||
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
|
||||
break;
|
||||
case BITS_PARITY_SPACE:
|
||||
dbg("%s - parity = SPACE (not supported, disabling parity)",
|
||||
__func__);
|
||||
cflag &= ~PARENB;
|
||||
bits &= ~BITS_PARITY_MASK;
|
||||
cp2101_set_config(port, CP2101_BITS, &bits, 2);
|
||||
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
|
||||
break;
|
||||
default:
|
||||
dbg("%s - Unknown parity mode, disabling parity", __func__);
|
||||
cflag &= ~PARENB;
|
||||
bits &= ~BITS_PARITY_MASK;
|
||||
cp2101_set_config(port, CP2101_BITS, &bits, 2);
|
||||
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -542,7 +561,7 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
|
||||
dbg("%s - stop bits = 1.5 (not supported, using 1 stop bit)",
|
||||
__func__);
|
||||
bits &= ~BITS_STOP_MASK;
|
||||
cp2101_set_config(port, CP2101_BITS, &bits, 2);
|
||||
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
|
||||
break;
|
||||
case BITS_STOP_2:
|
||||
dbg("%s - stop bits = 2", __func__);
|
||||
@ -552,11 +571,11 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
|
||||
dbg("%s - Unknown number of stop bits, using 1 stop bit",
|
||||
__func__);
|
||||
bits &= ~BITS_STOP_MASK;
|
||||
cp2101_set_config(port, CP2101_BITS, &bits, 2);
|
||||
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
cp2101_get_config(port, CP2101_MODEMCTL, modem_ctl, 16);
|
||||
cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
|
||||
if (modem_ctl[0] & 0x0008) {
|
||||
dbg("%s - flow control = CRTSCTS", __func__);
|
||||
cflag |= CRTSCTS;
|
||||
@ -568,7 +587,7 @@ static void cp2101_get_termios_port(struct usb_serial_port *port,
|
||||
*cflagp = cflag;
|
||||
}
|
||||
|
||||
static void cp2101_set_termios(struct tty_struct *tty,
|
||||
static void cp210x_set_termios(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct ktermios *old_termios)
|
||||
{
|
||||
unsigned int cflag, old_cflag;
|
||||
@ -583,13 +602,13 @@ static void cp2101_set_termios(struct tty_struct *tty,
|
||||
tty->termios->c_cflag &= ~CMSPAR;
|
||||
cflag = tty->termios->c_cflag;
|
||||
old_cflag = old_termios->c_cflag;
|
||||
baud = cp2101_quantise_baudrate(tty_get_baud_rate(tty));
|
||||
baud = cp210x_quantise_baudrate(tty_get_baud_rate(tty));
|
||||
|
||||
/* If the baud rate is to be updated*/
|
||||
if (baud != tty_termios_baud_rate(old_termios) && baud != 0) {
|
||||
dbg("%s - Setting baud rate to %d baud", __func__,
|
||||
baud);
|
||||
if (cp2101_set_config_single(port, CP2101_BAUDRATE,
|
||||
if (cp210x_set_config_single(port, CP210X_SET_BAUDDIV,
|
||||
((BAUD_RATE_GEN_FREQ + baud/2) / baud))) {
|
||||
dbg("Baud rate requested not supported by device\n");
|
||||
baud = tty_termios_baud_rate(old_termios);
|
||||
@ -600,7 +619,7 @@ static void cp2101_set_termios(struct tty_struct *tty,
|
||||
|
||||
/* If the number of data bits is to be updated */
|
||||
if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
|
||||
cp2101_get_config(port, CP2101_BITS, &bits, 2);
|
||||
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
|
||||
bits &= ~BITS_DATA_MASK;
|
||||
switch (cflag & CSIZE) {
|
||||
case CS5:
|
||||
@ -624,19 +643,19 @@ static void cp2101_set_termios(struct tty_struct *tty,
|
||||
dbg("%s - data bits = 9", __func__);
|
||||
break;*/
|
||||
default:
|
||||
dbg("cp2101 driver does not "
|
||||
dbg("cp210x driver does not "
|
||||
"support the number of bits requested,"
|
||||
" using 8 bit mode\n");
|
||||
bits |= BITS_DATA_8;
|
||||
break;
|
||||
}
|
||||
if (cp2101_set_config(port, CP2101_BITS, &bits, 2))
|
||||
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
|
||||
dbg("Number of data bits requested "
|
||||
"not supported by device\n");
|
||||
}
|
||||
|
||||
if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))) {
|
||||
cp2101_get_config(port, CP2101_BITS, &bits, 2);
|
||||
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
|
||||
bits &= ~BITS_PARITY_MASK;
|
||||
if (cflag & PARENB) {
|
||||
if (cflag & PARODD) {
|
||||
@ -647,13 +666,13 @@ static void cp2101_set_termios(struct tty_struct *tty,
|
||||
dbg("%s - parity = EVEN", __func__);
|
||||
}
|
||||
}
|
||||
if (cp2101_set_config(port, CP2101_BITS, &bits, 2))
|
||||
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
|
||||
dbg("Parity mode not supported "
|
||||
"by device\n");
|
||||
}
|
||||
|
||||
if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
|
||||
cp2101_get_config(port, CP2101_BITS, &bits, 2);
|
||||
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
|
||||
bits &= ~BITS_STOP_MASK;
|
||||
if (cflag & CSTOPB) {
|
||||
bits |= BITS_STOP_2;
|
||||
@ -662,13 +681,13 @@ static void cp2101_set_termios(struct tty_struct *tty,
|
||||
bits |= BITS_STOP_1;
|
||||
dbg("%s - stop bits = 1", __func__);
|
||||
}
|
||||
if (cp2101_set_config(port, CP2101_BITS, &bits, 2))
|
||||
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
|
||||
dbg("Number of stop bits requested "
|
||||
"not supported by device\n");
|
||||
}
|
||||
|
||||
if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
|
||||
cp2101_get_config(port, CP2101_MODEMCTL, modem_ctl, 16);
|
||||
cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
|
||||
dbg("%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x",
|
||||
__func__, modem_ctl[0], modem_ctl[1],
|
||||
modem_ctl[2], modem_ctl[3]);
|
||||
@ -688,19 +707,19 @@ static void cp2101_set_termios(struct tty_struct *tty,
|
||||
dbg("%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x",
|
||||
__func__, modem_ctl[0], modem_ctl[1],
|
||||
modem_ctl[2], modem_ctl[3]);
|
||||
cp2101_set_config(port, CP2101_MODEMCTL, modem_ctl, 16);
|
||||
cp210x_set_config(port, CP210X_SET_FLOW, modem_ctl, 16);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int cp2101_tiocmset (struct tty_struct *tty, struct file *file,
|
||||
static int cp210x_tiocmset (struct tty_struct *tty, struct file *file,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
return cp2101_tiocmset_port(port, file, set, clear);
|
||||
return cp210x_tiocmset_port(port, file, set, clear);
|
||||
}
|
||||
|
||||
static int cp2101_tiocmset_port(struct usb_serial_port *port, struct file *file,
|
||||
static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *file,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
unsigned int control = 0;
|
||||
@ -726,10 +745,10 @@ static int cp2101_tiocmset_port(struct usb_serial_port *port, struct file *file,
|
||||
|
||||
dbg("%s - control = 0x%.4x", __func__, control);
|
||||
|
||||
return cp2101_set_config(port, CP2101_CONTROL, &control, 2);
|
||||
return cp210x_set_config(port, CP210X_SET_MHS, &control, 2);
|
||||
}
|
||||
|
||||
static int cp2101_tiocmget (struct tty_struct *tty, struct file *file)
|
||||
static int cp210x_tiocmget (struct tty_struct *tty, struct file *file)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
unsigned int control;
|
||||
@ -737,7 +756,7 @@ static int cp2101_tiocmget (struct tty_struct *tty, struct file *file)
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
cp2101_get_config(port, CP2101_CONTROL, &control, 1);
|
||||
cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1);
|
||||
|
||||
result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0)
|
||||
|((control & CONTROL_RTS) ? TIOCM_RTS : 0)
|
||||
@ -751,7 +770,7 @@ static int cp2101_tiocmget (struct tty_struct *tty, struct file *file)
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cp2101_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;
|
||||
unsigned int state;
|
||||
@ -763,17 +782,17 @@ static void cp2101_break_ctl (struct tty_struct *tty, int break_state)
|
||||
state = BREAK_ON;
|
||||
dbg("%s - turning break %s", __func__,
|
||||
state == BREAK_OFF ? "off" : "on");
|
||||
cp2101_set_config(port, CP2101_BREAK, &state, 2);
|
||||
cp210x_set_config(port, CP210X_SET_BREAK, &state, 2);
|
||||
}
|
||||
|
||||
static int cp2101_startup(struct usb_serial *serial)
|
||||
static int cp210x_startup(struct usb_serial *serial)
|
||||
{
|
||||
/* CP2101 buffers behave strangely unless device is reset */
|
||||
/* cp210x buffers behave strangely unless device is reset */
|
||||
usb_reset_device(serial->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cp2101_shutdown(struct usb_serial *serial)
|
||||
static void cp210x_shutdown(struct usb_serial *serial)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -781,21 +800,21 @@ static void cp2101_shutdown(struct usb_serial *serial)
|
||||
|
||||
/* Stop reads and writes on all ports */
|
||||
for (i = 0; i < serial->num_ports; ++i)
|
||||
cp2101_cleanup(serial->port[i]);
|
||||
cp210x_cleanup(serial->port[i]);
|
||||
}
|
||||
|
||||
static int __init cp2101_init(void)
|
||||
static int __init cp210x_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = usb_serial_register(&cp2101_device);
|
||||
retval = usb_serial_register(&cp210x_device);
|
||||
if (retval)
|
||||
return retval; /* Failed to register */
|
||||
|
||||
retval = usb_register(&cp2101_driver);
|
||||
retval = usb_register(&cp210x_driver);
|
||||
if (retval) {
|
||||
/* Failed to register */
|
||||
usb_serial_deregister(&cp2101_device);
|
||||
usb_serial_deregister(&cp210x_device);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -805,14 +824,14 @@ static int __init cp2101_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cp2101_exit(void)
|
||||
static void __exit cp210x_exit(void)
|
||||
{
|
||||
usb_deregister(&cp2101_driver);
|
||||
usb_serial_deregister(&cp2101_device);
|
||||
usb_deregister(&cp210x_driver);
|
||||
usb_serial_deregister(&cp210x_device);
|
||||
}
|
||||
|
||||
module_init(cp2101_init);
|
||||
module_exit(cp2101_exit);
|
||||
module_init(cp210x_init);
|
||||
module_exit(cp210x_exit);
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
|
@ -61,8 +61,7 @@ static int cyberjack_startup(struct usb_serial *serial);
|
||||
static void cyberjack_shutdown(struct usb_serial *serial);
|
||||
static int cyberjack_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void cyberjack_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
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);
|
||||
@ -185,8 +184,7 @@ static int cyberjack_open(struct tty_struct *tty,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cyberjack_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void cyberjack_close(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
|
@ -174,8 +174,8 @@ static int cypress_ca42v2_startup(struct usb_serial *serial);
|
||||
static void cypress_shutdown(struct usb_serial *serial);
|
||||
static int cypress_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void cypress_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void cypress_close(struct usb_serial_port *port);
|
||||
static void cypress_dtr_rts(struct usb_serial_port *port, int on);
|
||||
static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
static void cypress_send(struct usb_serial_port *port);
|
||||
@ -218,6 +218,7 @@ static struct usb_serial_driver cypress_earthmate_device = {
|
||||
.shutdown = cypress_shutdown,
|
||||
.open = cypress_open,
|
||||
.close = cypress_close,
|
||||
.dtr_rts = cypress_dtr_rts,
|
||||
.write = cypress_write,
|
||||
.write_room = cypress_write_room,
|
||||
.ioctl = cypress_ioctl,
|
||||
@ -244,6 +245,7 @@ static struct usb_serial_driver cypress_hidcom_device = {
|
||||
.shutdown = cypress_shutdown,
|
||||
.open = cypress_open,
|
||||
.close = cypress_close,
|
||||
.dtr_rts = cypress_dtr_rts,
|
||||
.write = cypress_write,
|
||||
.write_room = cypress_write_room,
|
||||
.ioctl = cypress_ioctl,
|
||||
@ -270,6 +272,7 @@ static struct usb_serial_driver cypress_ca42v2_device = {
|
||||
.shutdown = cypress_shutdown,
|
||||
.open = cypress_open,
|
||||
.close = cypress_close,
|
||||
.dtr_rts = cypress_dtr_rts,
|
||||
.write = cypress_write,
|
||||
.write_room = cypress_write_room,
|
||||
.ioctl = cypress_ioctl,
|
||||
@ -656,11 +659,7 @@ static int cypress_open(struct tty_struct *tty,
|
||||
priv->rx_flags = 0;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
/* raise both lines and set termios */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->line_control = CONTROL_DTR | CONTROL_RTS;
|
||||
priv->cmd_ctrl = 1;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
/* Set termios */
|
||||
result = cypress_write(tty, port, NULL, 0);
|
||||
|
||||
if (result) {
|
||||
@ -694,76 +693,42 @@ static int cypress_open(struct tty_struct *tty,
|
||||
__func__, result);
|
||||
cypress_set_dead(port);
|
||||
}
|
||||
|
||||
port->port.drain_delay = 256;
|
||||
return result;
|
||||
} /* cypress_open */
|
||||
|
||||
|
||||
static void cypress_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void cypress_dtr_rts(struct usb_serial_port *port, int on)
|
||||
{
|
||||
struct cypress_private *priv = usb_get_serial_port_data(port);
|
||||
/* drop dtr and rts */
|
||||
priv = usb_get_serial_port_data(port);
|
||||
spin_lock_irq(&priv->lock);
|
||||
if (on == 0)
|
||||
priv->line_control = 0;
|
||||
else
|
||||
priv->line_control = CONTROL_DTR | CONTROL_RTS;
|
||||
priv->cmd_ctrl = 1;
|
||||
spin_unlock_irq(&priv->lock);
|
||||
cypress_write(NULL, port, NULL, 0);
|
||||
}
|
||||
|
||||
static void cypress_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct cypress_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned int c_cflag;
|
||||
int bps;
|
||||
long timeout;
|
||||
wait_queue_t wait;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
/* wait for data to drain from buffer */
|
||||
spin_lock_irq(&priv->lock);
|
||||
timeout = CYPRESS_CLOSING_WAIT;
|
||||
init_waitqueue_entry(&wait, current);
|
||||
add_wait_queue(&tty->write_wait, &wait);
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (cypress_buf_data_avail(priv->buf) == 0
|
||||
|| timeout == 0 || signal_pending(current)
|
||||
/* without mutex, allowed due to harmless failure mode */
|
||||
|| port->serial->disconnected)
|
||||
break;
|
||||
spin_unlock_irq(&priv->lock);
|
||||
timeout = schedule_timeout(timeout);
|
||||
spin_lock_irq(&priv->lock);
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&tty->write_wait, &wait);
|
||||
/* clear out any remaining data in the buffer */
|
||||
cypress_buf_clear(priv->buf);
|
||||
spin_unlock_irq(&priv->lock);
|
||||
|
||||
/* writing is potentially harmful, lock must be taken */
|
||||
mutex_lock(&port->serial->disc_mutex);
|
||||
if (port->serial->disconnected) {
|
||||
mutex_unlock(&port->serial->disc_mutex);
|
||||
return;
|
||||
}
|
||||
/* wait for characters to drain from device */
|
||||
if (tty) {
|
||||
bps = tty_get_baud_rate(tty);
|
||||
if (bps > 1200)
|
||||
timeout = max((HZ * 2560) / bps, HZ / 10);
|
||||
else
|
||||
timeout = 2 * HZ;
|
||||
schedule_timeout_interruptible(timeout);
|
||||
}
|
||||
|
||||
cypress_buf_clear(priv->buf);
|
||||
dbg("%s - stopping urbs", __func__);
|
||||
usb_kill_urb(port->interrupt_in_urb);
|
||||
usb_kill_urb(port->interrupt_out_urb);
|
||||
|
||||
if (tty) {
|
||||
c_cflag = tty->termios->c_cflag;
|
||||
if (c_cflag & HUPCL) {
|
||||
/* drop dtr and rts */
|
||||
priv = usb_get_serial_port_data(port);
|
||||
spin_lock_irq(&priv->lock);
|
||||
priv->line_control = 0;
|
||||
priv->cmd_ctrl = 1;
|
||||
spin_unlock_irq(&priv->lock);
|
||||
cypress_write(tty, port, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (stats)
|
||||
dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n",
|
||||
|
@ -422,7 +422,6 @@ struct digi_port {
|
||||
int dp_throttled;
|
||||
int dp_throttle_restart;
|
||||
wait_queue_head_t dp_flush_wait;
|
||||
int dp_in_close; /* close in progress */
|
||||
wait_queue_head_t dp_close_wait; /* wait queue for close */
|
||||
struct work_struct dp_wakeup_work;
|
||||
struct usb_serial_port *dp_port;
|
||||
@ -456,8 +455,9 @@ static int digi_write_room(struct tty_struct *tty);
|
||||
static int digi_chars_in_buffer(struct tty_struct *tty);
|
||||
static int digi_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void digi_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void digi_close(struct usb_serial_port *port);
|
||||
static int digi_carrier_raised(struct usb_serial_port *port);
|
||||
static void digi_dtr_rts(struct usb_serial_port *port, int on);
|
||||
static int digi_startup_device(struct usb_serial *serial);
|
||||
static int digi_startup(struct usb_serial *serial);
|
||||
static void digi_shutdown(struct usb_serial *serial);
|
||||
@ -510,6 +510,8 @@ static struct usb_serial_driver digi_acceleport_2_device = {
|
||||
.num_ports = 3,
|
||||
.open = digi_open,
|
||||
.close = digi_close,
|
||||
.dtr_rts = digi_dtr_rts,
|
||||
.carrier_raised = digi_carrier_raised,
|
||||
.write = digi_write,
|
||||
.write_room = digi_write_room,
|
||||
.write_bulk_callback = digi_write_bulk_callback,
|
||||
@ -1328,6 +1330,19 @@ static int digi_chars_in_buffer(struct tty_struct *tty)
|
||||
|
||||
}
|
||||
|
||||
static void digi_dtr_rts(struct usb_serial_port *port, int on)
|
||||
{
|
||||
/* Adjust DTR and RTS */
|
||||
digi_set_modem_signals(port, on * (TIOCM_DTR|TIOCM_RTS), 1);
|
||||
}
|
||||
|
||||
static int digi_carrier_raised(struct usb_serial_port *port)
|
||||
{
|
||||
struct digi_port *priv = usb_get_serial_port_data(port);
|
||||
if (priv->dp_modem_signals & TIOCM_CD)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int digi_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp)
|
||||
@ -1336,7 +1351,6 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
unsigned char buf[32];
|
||||
struct digi_port *priv = usb_get_serial_port_data(port);
|
||||
struct ktermios not_termios;
|
||||
unsigned long flags = 0;
|
||||
|
||||
dbg("digi_open: TOP: port=%d, open_count=%d",
|
||||
priv->dp_port_num, port->port.count);
|
||||
@ -1345,26 +1359,6 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
if (digi_startup_device(port->serial) != 0)
|
||||
return -ENXIO;
|
||||
|
||||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||||
|
||||
/* don't wait on a close in progress for non-blocking opens */
|
||||
if (priv->dp_in_close && (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0) {
|
||||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* wait for a close in progress to finish */
|
||||
while (priv->dp_in_close) {
|
||||
cond_wait_interruptible_timeout_irqrestore(
|
||||
&priv->dp_close_wait, DIGI_RETRY_TIMEOUT,
|
||||
&priv->dp_port_lock, flags);
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||||
|
||||
/* read modem signals automatically whenever they change */
|
||||
buf[0] = DIGI_CMD_READ_INPUT_SIGNALS;
|
||||
buf[1] = priv->dp_port_num;
|
||||
@ -1387,16 +1381,11 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
not_termios.c_iflag = ~tty->termios->c_iflag;
|
||||
digi_set_termios(tty, port, ¬_termios);
|
||||
}
|
||||
|
||||
/* set DTR and RTS */
|
||||
digi_set_modem_signals(port, TIOCM_DTR|TIOCM_RTS, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void digi_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp)
|
||||
static void digi_close(struct usb_serial_port *port)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
int ret;
|
||||
@ -1411,28 +1400,9 @@ static void digi_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
if (port->serial->disconnected)
|
||||
goto exit;
|
||||
|
||||
/* do cleanup only after final close on this port */
|
||||
spin_lock_irq(&priv->dp_port_lock);
|
||||
priv->dp_in_close = 1;
|
||||
spin_unlock_irq(&priv->dp_port_lock);
|
||||
|
||||
/* tell line discipline to process only XON/XOFF */
|
||||
tty->closing = 1;
|
||||
|
||||
/* wait for output to drain */
|
||||
if ((filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0)
|
||||
tty_wait_until_sent(tty, DIGI_CLOSE_TIMEOUT);
|
||||
|
||||
/* flush driver and line discipline buffers */
|
||||
tty_driver_flush_buffer(tty);
|
||||
tty_ldisc_flush(tty);
|
||||
|
||||
if (port->serial->dev) {
|
||||
/* wait for transmit idle */
|
||||
if ((filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0)
|
||||
digi_transmit_idle(port, DIGI_CLOSE_TIMEOUT);
|
||||
/* drop DTR and RTS */
|
||||
digi_set_modem_signals(port, 0, 0);
|
||||
/* FIXME: Transmit idle belongs in the wait_unti_sent path */
|
||||
digi_transmit_idle(port, DIGI_CLOSE_TIMEOUT);
|
||||
|
||||
/* disable input flow control */
|
||||
buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
|
||||
@ -1477,11 +1447,9 @@ static void digi_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
/* shutdown any outstanding bulk writes */
|
||||
usb_kill_urb(port->write_urb);
|
||||
}
|
||||
tty->closing = 0;
|
||||
exit:
|
||||
spin_lock_irq(&priv->dp_port_lock);
|
||||
priv->dp_write_urb_in_use = 0;
|
||||
priv->dp_in_close = 0;
|
||||
wake_up_interruptible(&priv->dp_close_wait);
|
||||
spin_unlock_irq(&priv->dp_port_lock);
|
||||
mutex_unlock(&port->serial->disc_mutex);
|
||||
@ -1560,7 +1528,6 @@ static int digi_startup(struct usb_serial *serial)
|
||||
priv->dp_throttled = 0;
|
||||
priv->dp_throttle_restart = 0;
|
||||
init_waitqueue_head(&priv->dp_flush_wait);
|
||||
priv->dp_in_close = 0;
|
||||
init_waitqueue_head(&priv->dp_close_wait);
|
||||
INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock);
|
||||
priv->dp_port = serial->port[i];
|
||||
|
@ -81,8 +81,7 @@ static int debug;
|
||||
/* function prototypes for an empeg-car player */
|
||||
static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void empeg_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void empeg_close(struct usb_serial_port *port);
|
||||
static int empeg_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *buf,
|
||||
int count);
|
||||
@ -181,8 +180,7 @@ static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
}
|
||||
|
||||
|
||||
static void empeg_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp)
|
||||
static void empeg_close(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
|
@ -89,6 +89,7 @@ struct ftdi_private {
|
||||
int force_rtscts; /* if non-zero, force RTS-CTS to always
|
||||
be enabled */
|
||||
|
||||
unsigned int latency; /* latency setting in use */
|
||||
spinlock_t tx_lock; /* spinlock for transmit state */
|
||||
unsigned long tx_bytes;
|
||||
unsigned long tx_outstanding_bytes;
|
||||
@ -719,8 +720,8 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port);
|
||||
static int ftdi_sio_port_remove(struct usb_serial_port *port);
|
||||
static int ftdi_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void ftdi_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void ftdi_close(struct usb_serial_port *port);
|
||||
static void ftdi_dtr_rts(struct usb_serial_port *port, int on);
|
||||
static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
static int ftdi_write_room(struct tty_struct *tty);
|
||||
@ -758,6 +759,7 @@ static struct usb_serial_driver ftdi_sio_device = {
|
||||
.port_remove = ftdi_sio_port_remove,
|
||||
.open = ftdi_open,
|
||||
.close = ftdi_close,
|
||||
.dtr_rts = ftdi_dtr_rts,
|
||||
.throttle = ftdi_throttle,
|
||||
.unthrottle = ftdi_unthrottle,
|
||||
.write = ftdi_write,
|
||||
@ -1037,7 +1039,54 @@ static int change_speed(struct tty_struct *tty, struct usb_serial_port *port)
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int write_latency_timer(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
struct usb_device *udev = port->serial->dev;
|
||||
char buf[1];
|
||||
int rv = 0;
|
||||
int l = priv->latency;
|
||||
|
||||
if (priv->flags & ASYNC_LOW_LATENCY)
|
||||
l = 1;
|
||||
|
||||
dbg("%s: setting latency timer = %i", __func__, l);
|
||||
|
||||
rv = usb_control_msg(udev,
|
||||
usb_sndctrlpipe(udev, 0),
|
||||
FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
|
||||
FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
|
||||
l, priv->interface,
|
||||
buf, 0, WDR_TIMEOUT);
|
||||
|
||||
if (rv < 0)
|
||||
dev_err(&port->dev, "Unable to write latency timer: %i\n", rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int read_latency_timer(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
struct usb_device *udev = port->serial->dev;
|
||||
unsigned short latency = 0;
|
||||
int rv = 0;
|
||||
|
||||
|
||||
dbg("%s", __func__);
|
||||
|
||||
rv = usb_control_msg(udev,
|
||||
usb_rcvctrlpipe(udev, 0),
|
||||
FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
|
||||
FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
|
||||
0, priv->interface,
|
||||
(char *) &latency, 1, WDR_TIMEOUT);
|
||||
|
||||
if (rv < 0) {
|
||||
dev_err(&port->dev, "Unable to read latency timer: %i\n", rv);
|
||||
return -EIO;
|
||||
}
|
||||
return latency;
|
||||
}
|
||||
|
||||
static int get_serial_info(struct usb_serial_port *port,
|
||||
struct serial_struct __user *retinfo)
|
||||
@ -1097,6 +1146,7 @@ static int set_serial_info(struct tty_struct *tty,
|
||||
priv->custom_divisor = new_serial.custom_divisor;
|
||||
|
||||
tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
|
||||
write_latency_timer(port);
|
||||
|
||||
check_and_exit:
|
||||
if ((old_priv.flags & ASYNC_SPD_MASK) !=
|
||||
@ -1192,27 +1242,13 @@ static ssize_t show_latency_timer(struct device *dev,
|
||||
{
|
||||
struct usb_serial_port *port = to_usb_serial_port(dev);
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
struct usb_device *udev = port->serial->dev;
|
||||
unsigned short latency = 0;
|
||||
int rv = 0;
|
||||
|
||||
|
||||
dbg("%s", __func__);
|
||||
|
||||
rv = usb_control_msg(udev,
|
||||
usb_rcvctrlpipe(udev, 0),
|
||||
FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
|
||||
FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
|
||||
0, priv->interface,
|
||||
(char *) &latency, 1, WDR_TIMEOUT);
|
||||
|
||||
if (rv < 0) {
|
||||
dev_err(dev, "Unable to read latency timer: %i\n", rv);
|
||||
return -EIO;
|
||||
}
|
||||
return sprintf(buf, "%i\n", latency);
|
||||
if (priv->flags & ASYNC_LOW_LATENCY)
|
||||
return sprintf(buf, "1\n");
|
||||
else
|
||||
return sprintf(buf, "%i\n", priv->latency);
|
||||
}
|
||||
|
||||
|
||||
/* Write a new value of the latency timer, in units of milliseconds. */
|
||||
static ssize_t store_latency_timer(struct device *dev,
|
||||
struct device_attribute *attr, const char *valbuf,
|
||||
@ -1220,25 +1256,13 @@ static ssize_t store_latency_timer(struct device *dev,
|
||||
{
|
||||
struct usb_serial_port *port = to_usb_serial_port(dev);
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
struct usb_device *udev = port->serial->dev;
|
||||
char buf[1];
|
||||
int v = simple_strtoul(valbuf, NULL, 10);
|
||||
int rv = 0;
|
||||
|
||||
dbg("%s: setting latency timer = %i", __func__, v);
|
||||
|
||||
rv = usb_control_msg(udev,
|
||||
usb_sndctrlpipe(udev, 0),
|
||||
FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
|
||||
FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
|
||||
v, priv->interface,
|
||||
buf, 0, WDR_TIMEOUT);
|
||||
|
||||
if (rv < 0) {
|
||||
dev_err(dev, "Unable to write latency timer: %i\n", rv);
|
||||
priv->latency = v;
|
||||
rv = write_latency_timer(port);
|
||||
if (rv < 0)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -1392,6 +1416,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
|
||||
usb_set_serial_port_data(port, priv);
|
||||
|
||||
ftdi_determine_type(port);
|
||||
read_latency_timer(port);
|
||||
create_sysfs_attrs(port);
|
||||
return 0;
|
||||
}
|
||||
@ -1514,6 +1539,8 @@ static int ftdi_open(struct tty_struct *tty,
|
||||
if (tty)
|
||||
tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
|
||||
|
||||
write_latency_timer(port);
|
||||
|
||||
/* No error checking for this (will get errors later anyway) */
|
||||
/* See ftdi_sio.h for description of what is reset */
|
||||
usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
@ -1529,11 +1556,6 @@ static int ftdi_open(struct tty_struct *tty,
|
||||
if (tty)
|
||||
ftdi_set_termios(tty, port, tty->termios);
|
||||
|
||||
/* FIXME: Flow control might be enabled, so it should be checked -
|
||||
we have no control of defaults! */
|
||||
/* Turn on RTS and DTR since we are not flow controlling by default */
|
||||
set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
|
||||
|
||||
/* Not throttled */
|
||||
spin_lock_irqsave(&priv->rx_lock, flags);
|
||||
priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
|
||||
@ -1558,6 +1580,30 @@ static int ftdi_open(struct tty_struct *tty,
|
||||
} /* ftdi_open */
|
||||
|
||||
|
||||
static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
char buf[1];
|
||||
|
||||
mutex_lock(&port->serial->disc_mutex);
|
||||
if (!port->serial->disconnected) {
|
||||
/* Disable flow control */
|
||||
if (!on && usb_control_msg(port->serial->dev,
|
||||
usb_sndctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
|
||||
0, priv->interface, buf, 0,
|
||||
WDR_TIMEOUT) < 0) {
|
||||
dev_err(&port->dev, "error from flowcontrol urb\n");
|
||||
}
|
||||
/* drop RTS and DTR */
|
||||
if (on)
|
||||
set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
|
||||
else
|
||||
clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
|
||||
}
|
||||
mutex_unlock(&port->serial->disc_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* usbserial:__serial_close only calls ftdi_close if the point is open
|
||||
@ -1567,31 +1613,12 @@ static int ftdi_open(struct tty_struct *tty,
|
||||
*
|
||||
*/
|
||||
|
||||
static void ftdi_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void ftdi_close(struct usb_serial_port *port)
|
||||
{ /* ftdi_close */
|
||||
unsigned int c_cflag = tty->termios->c_cflag;
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
char buf[1];
|
||||
|
||||
dbg("%s", __func__);
|
||||
|
||||
mutex_lock(&port->serial->disc_mutex);
|
||||
if (c_cflag & HUPCL && !port->serial->disconnected) {
|
||||
/* Disable flow control */
|
||||
if (usb_control_msg(port->serial->dev,
|
||||
usb_sndctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
|
||||
0, priv->interface, buf, 0,
|
||||
WDR_TIMEOUT) < 0) {
|
||||
dev_err(&port->dev, "error from flowcontrol urb\n");
|
||||
}
|
||||
|
||||
/* drop RTS and DTR */
|
||||
clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
|
||||
} /* Note change no line if hupcl is off */
|
||||
mutex_unlock(&port->serial->disc_mutex);
|
||||
|
||||
/* cancel any scheduled reading */
|
||||
cancel_delayed_work_sync(&priv->rx_work);
|
||||
|
@ -993,8 +993,7 @@ static int garmin_open(struct tty_struct *tty,
|
||||
}
|
||||
|
||||
|
||||
static void garmin_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void garmin_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
|
||||
|
@ -184,8 +184,7 @@ int usb_serial_generic_resume(struct usb_serial *serial)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_serial_generic_resume);
|
||||
|
||||
void usb_serial_generic_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
void usb_serial_generic_close(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
generic_cleanup(port);
|
||||
|
@ -207,8 +207,7 @@ static void edge_bulk_out_cmd_callback(struct urb *urb);
|
||||
/* function prototypes for the usbserial callbacks */
|
||||
static int edge_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void edge_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void edge_close(struct usb_serial_port *port);
|
||||
static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
static int edge_write_room(struct tty_struct *tty);
|
||||
@ -965,7 +964,7 @@ static int edge_open(struct tty_struct *tty,
|
||||
|
||||
if (!edge_port->txfifo.fifo) {
|
||||
dbg("%s - no memory", __func__);
|
||||
edge_close(tty, port, filp);
|
||||
edge_close(port);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -975,7 +974,7 @@ static int edge_open(struct tty_struct *tty,
|
||||
|
||||
if (!edge_port->write_urb) {
|
||||
dbg("%s - no memory", __func__);
|
||||
edge_close(tty, port, filp);
|
||||
edge_close(port);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -1099,8 +1098,7 @@ static void block_until_tx_empty(struct edgeport_port *edge_port)
|
||||
* edge_close
|
||||
* this function is called by the tty driver when a port is closed
|
||||
*****************************************************************************/
|
||||
static void edge_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void edge_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct edgeport_serial *edge_serial;
|
||||
struct edgeport_port *edge_port;
|
||||
|
@ -2009,8 +2009,7 @@ release_es_lock:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void edge_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void edge_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct edgeport_serial *edge_serial;
|
||||
struct edgeport_port *edge_port;
|
||||
|
@ -76,8 +76,7 @@ static int initial_wait;
|
||||
/* Function prototypes for an ipaq */
|
||||
static int ipaq_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void ipaq_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void ipaq_close(struct usb_serial_port *port);
|
||||
static int ipaq_calc_num_ports(struct usb_serial *serial);
|
||||
static int ipaq_startup(struct usb_serial *serial);
|
||||
static void ipaq_shutdown(struct usb_serial *serial);
|
||||
@ -714,8 +713,7 @@ error:
|
||||
}
|
||||
|
||||
|
||||
static void ipaq_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void ipaq_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct ipaq_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
|
@ -302,23 +302,17 @@ static int ipw_open(struct tty_struct *tty,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipw_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void ipw_dtr_rts(struct usb_serial_port *port, int on)
|
||||
{
|
||||
struct usb_device *dev = port->serial->dev;
|
||||
int result;
|
||||
|
||||
if (tty_hung_up_p(filp)) {
|
||||
dbg("%s: tty_hung_up_p ...", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/*--1: drop the dtr */
|
||||
dbg("%s:dropping dtr", __func__);
|
||||
result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
IPW_SIO_SET_PIN,
|
||||
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
IPW_PIN_CLRDTR,
|
||||
on ? IPW_PIN_SETDTR : IPW_PIN_CLRDTR,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
@ -332,7 +326,7 @@ static void ipw_close(struct tty_struct *tty,
|
||||
result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
IPW_SIO_SET_PIN, USB_TYPE_VENDOR |
|
||||
USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
IPW_PIN_CLRRTS,
|
||||
on ? IPW_PIN_SETRTS : IPW_PIN_CLRRTS,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
@ -340,7 +334,12 @@ static void ipw_close(struct tty_struct *tty,
|
||||
if (result < 0)
|
||||
dev_err(&port->dev,
|
||||
"dropping rts failed (error = %d)\n", result);
|
||||
}
|
||||
|
||||
static void ipw_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_device *dev = port->serial->dev;
|
||||
int result;
|
||||
|
||||
/*--3: purge */
|
||||
dbg("%s:sending purge", __func__);
|
||||
@ -461,6 +460,7 @@ static struct usb_serial_driver ipw_device = {
|
||||
.num_ports = 1,
|
||||
.open = ipw_open,
|
||||
.close = ipw_close,
|
||||
.dtr_rts = ipw_dtr_rts,
|
||||
.port_probe = ipw_probe,
|
||||
.port_remove = ipw_disconnect,
|
||||
.write = ipw_write,
|
||||
|
@ -88,8 +88,7 @@ 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,
|
||||
struct file *filep);
|
||||
static void ir_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filep);
|
||||
static void ir_close(struct usb_serial_port *port);
|
||||
static int ir_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
static void ir_write_bulk_callback (struct urb *urb);
|
||||
@ -346,8 +345,7 @@ static int ir_open(struct tty_struct *tty,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ir_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file * filp)
|
||||
static void ir_close(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
|
@ -40,7 +40,7 @@ static int debug;
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v0.5"
|
||||
#define DRIVER_VERSION "v0.10"
|
||||
#define DRIVER_DESC "Infinity USB Unlimited Phoenix driver"
|
||||
|
||||
static struct usb_device_id id_table[] = {
|
||||
@ -70,7 +70,6 @@ static void read_rxcmd_callback(struct urb *urb);
|
||||
struct iuu_private {
|
||||
spinlock_t lock; /* store irq state */
|
||||
wait_queue_head_t delta_msr_wait;
|
||||
u8 line_control;
|
||||
u8 line_status;
|
||||
u8 termios_initialized;
|
||||
int tiostatus; /* store IUART SIGNAL for tiocmget call */
|
||||
@ -651,32 +650,33 @@ static int iuu_bulk_write(struct usb_serial_port *port)
|
||||
unsigned long flags;
|
||||
int result;
|
||||
int i;
|
||||
int buf_len;
|
||||
char *buf_ptr = port->write_urb->transfer_buffer;
|
||||
dbg("%s - enter", __func__);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
*buf_ptr++ = IUU_UART_ESC;
|
||||
*buf_ptr++ = IUU_UART_TX;
|
||||
*buf_ptr++ = priv->writelen;
|
||||
|
||||
memcpy(buf_ptr, priv->writebuf,
|
||||
priv->writelen);
|
||||
memcpy(buf_ptr, priv->writebuf, priv->writelen);
|
||||
buf_len = priv->writelen;
|
||||
priv->writelen = 0;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
if (debug == 1) {
|
||||
for (i = 0; i < priv->writelen; i++)
|
||||
for (i = 0; i < buf_len; i++)
|
||||
sprintf(priv->dbgbuf + i*2 ,
|
||||
"%02X", priv->writebuf[i]);
|
||||
priv->dbgbuf[priv->writelen+i*2] = 0;
|
||||
priv->dbgbuf[buf_len+i*2] = 0;
|
||||
dbg("%s - writing %i chars : %s", __func__,
|
||||
priv->writelen, priv->dbgbuf);
|
||||
buf_len, priv->dbgbuf);
|
||||
}
|
||||
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
|
||||
usb_sndbulkpipe(port->serial->dev,
|
||||
port->bulk_out_endpointAddress),
|
||||
port->write_urb->transfer_buffer, priv->writelen + 3,
|
||||
port->write_urb->transfer_buffer, buf_len + 3,
|
||||
iuu_rxcmd, port);
|
||||
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->writelen = 0;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
usb_serial_port_softint(port);
|
||||
return result;
|
||||
}
|
||||
@ -770,14 +770,10 @@ static int iuu_uart_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (priv->writelen > 0) {
|
||||
/* buffer already filled but not commited */
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fill the buffer */
|
||||
memcpy(priv->writebuf, buf, count);
|
||||
priv->writelen = count;
|
||||
memcpy(priv->writebuf + priv->writelen, buf, count);
|
||||
priv->writelen += count;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return count;
|
||||
@ -819,7 +815,7 @@ static int iuu_uart_on(struct usb_serial_port *port)
|
||||
buf[0] = IUU_UART_ENABLE;
|
||||
buf[1] = (u8) ((IUU_BAUD_9600 >> 8) & 0x00FF);
|
||||
buf[2] = (u8) (0x00FF & IUU_BAUD_9600);
|
||||
buf[3] = (u8) (0x0F0 & IUU_TWO_STOP_BITS) | (0x07 & IUU_PARITY_EVEN);
|
||||
buf[3] = (u8) (0x0F0 & IUU_ONE_STOP_BIT) | (0x07 & IUU_PARITY_EVEN);
|
||||
|
||||
status = bulk_immediate(port, buf, 4);
|
||||
if (status != IUU_OPERATION_OK) {
|
||||
@ -946,19 +942,59 @@ static int iuu_uart_baud(struct usb_serial_port *port, u32 baud,
|
||||
return status;
|
||||
}
|
||||
|
||||
static int set_control_lines(struct usb_device *dev, u8 value)
|
||||
static void iuu_set_termios(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct ktermios *old_termios)
|
||||
{
|
||||
return 0;
|
||||
const u32 supported_mask = CMSPAR|PARENB|PARODD;
|
||||
|
||||
unsigned int cflag = tty->termios->c_cflag;
|
||||
int status;
|
||||
u32 actual;
|
||||
u32 parity;
|
||||
int csize = CS7;
|
||||
int baud = 9600; /* Fixed for the moment */
|
||||
u32 newval = cflag & supported_mask;
|
||||
|
||||
/* compute the parity parameter */
|
||||
parity = 0;
|
||||
if (cflag & CMSPAR) { /* Using mark space */
|
||||
if (cflag & PARODD)
|
||||
parity |= IUU_PARITY_SPACE;
|
||||
else
|
||||
parity |= IUU_PARITY_MARK;
|
||||
} else if (!(cflag & PARENB)) {
|
||||
parity |= IUU_PARITY_NONE;
|
||||
csize = CS8;
|
||||
} else if (cflag & PARODD)
|
||||
parity |= IUU_PARITY_ODD;
|
||||
else
|
||||
parity |= IUU_PARITY_EVEN;
|
||||
|
||||
parity |= (cflag & CSTOPB ? IUU_TWO_STOP_BITS : IUU_ONE_STOP_BIT);
|
||||
|
||||
/* set it */
|
||||
status = iuu_uart_baud(port,
|
||||
(clockmode == 2) ? 16457 : 9600 * boost / 100,
|
||||
&actual, parity);
|
||||
|
||||
/* set the termios value to the real one, so the user now what has
|
||||
* changed. We support few fields so its easies to copy the old hw
|
||||
* settings back over and then adjust them
|
||||
*/
|
||||
if (old_termios)
|
||||
tty_termios_copy_hw(tty->termios, old_termios);
|
||||
if (status != 0) /* Set failed - return old bits */
|
||||
return;
|
||||
/* Re-encode speed, parity and csize */
|
||||
tty_encode_baud_rate(tty, baud, baud);
|
||||
tty->termios->c_cflag &= ~(supported_mask|CSIZE);
|
||||
tty->termios->c_cflag |= newval | csize;
|
||||
}
|
||||
|
||||
static void iuu_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void iuu_close(struct usb_serial_port *port)
|
||||
{
|
||||
/* iuu_led (port,255,0,0,0); */
|
||||
struct usb_serial *serial;
|
||||
struct iuu_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned long flags;
|
||||
unsigned int c_cflag;
|
||||
|
||||
serial = port->serial;
|
||||
if (!serial)
|
||||
@ -968,17 +1004,6 @@ static void iuu_close(struct tty_struct *tty,
|
||||
|
||||
iuu_uart_off(port);
|
||||
if (serial->dev) {
|
||||
if (tty) {
|
||||
c_cflag = tty->termios->c_cflag;
|
||||
if (c_cflag & HUPCL) {
|
||||
/* drop DTR and RTS */
|
||||
priv = usb_get_serial_port_data(port);
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->line_control = 0;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
set_control_lines(port->serial->dev, 0);
|
||||
}
|
||||
}
|
||||
/* free writebuf */
|
||||
/* shutdown our urbs */
|
||||
dbg("%s - shutting down urbs", __func__);
|
||||
@ -1154,7 +1179,7 @@ static int iuu_open(struct tty_struct *tty,
|
||||
if (result) {
|
||||
dev_err(&port->dev, "%s - failed submitting read urb,"
|
||||
" error %d\n", __func__, result);
|
||||
iuu_close(tty, port, NULL);
|
||||
iuu_close(port);
|
||||
return -EPROTO;
|
||||
} else {
|
||||
dbg("%s - rxcmd OK", __func__);
|
||||
@ -1175,6 +1200,7 @@ static struct usb_serial_driver iuu_device = {
|
||||
.read_bulk_callback = iuu_uart_read_callback,
|
||||
.tiocmget = iuu_tiocmget,
|
||||
.tiocmset = iuu_tiocmset,
|
||||
.set_termios = iuu_set_termios,
|
||||
.attach = iuu_startup,
|
||||
.shutdown = iuu_shutdown,
|
||||
};
|
||||
|
@ -1298,8 +1298,16 @@ static inline void stop_urb(struct urb *urb)
|
||||
usb_kill_urb(urb);
|
||||
}
|
||||
|
||||
static void keyspan_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void keyspan_dtr_rts(struct usb_serial_port *port, int on)
|
||||
{
|
||||
struct keyspan_port_private *p_priv = usb_get_serial_port_data(port);
|
||||
|
||||
p_priv->rts_state = on;
|
||||
p_priv->dtr_state = on;
|
||||
keyspan_send_setup(port, 0);
|
||||
}
|
||||
|
||||
static void keyspan_close(struct usb_serial_port *port)
|
||||
{
|
||||
int i;
|
||||
struct usb_serial *serial = port->serial;
|
||||
@ -1336,7 +1344,6 @@ static void keyspan_close(struct tty_struct *tty,
|
||||
stop_urb(p_priv->out_urbs[i]);
|
||||
}
|
||||
}
|
||||
tty_port_tty_set(&port->port, NULL);
|
||||
}
|
||||
|
||||
/* download the firmware to a pre-renumeration device */
|
||||
|
@ -38,9 +38,8 @@
|
||||
static int keyspan_open (struct tty_struct *tty,
|
||||
struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void keyspan_close (struct tty_struct *tty,
|
||||
struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
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_shutdown (struct usb_serial *serial);
|
||||
static int keyspan_write_room (struct tty_struct *tty);
|
||||
@ -562,6 +561,7 @@ static struct usb_serial_driver keyspan_1port_device = {
|
||||
.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,
|
||||
@ -582,6 +582,7 @@ static struct usb_serial_driver keyspan_2port_device = {
|
||||
.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,
|
||||
@ -602,6 +603,7 @@ static struct usb_serial_driver keyspan_4port_device = {
|
||||
.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,
|
||||
|
@ -651,6 +651,35 @@ static int keyspan_pda_chars_in_buffer(struct tty_struct *tty)
|
||||
}
|
||||
|
||||
|
||||
static void keyspan_pda_dtr_rts(struct usb_serial_port *port, int on)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
|
||||
if (serial->dev) {
|
||||
if (on)
|
||||
keyspan_pda_set_modem_info(serial, (1<<7) | (1<< 2));
|
||||
else
|
||||
keyspan_pda_set_modem_info(serial, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int keyspan_pda_carrier_raised(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
unsigned char modembits;
|
||||
|
||||
/* If we can read the modem status and the DCD is low then
|
||||
carrier is not raised yet */
|
||||
if (keyspan_pda_get_modem_info(serial, &modembits) >= 0) {
|
||||
if (!(modembits & (1>>6)))
|
||||
return 0;
|
||||
}
|
||||
/* Carrier raised, or we failed (eg disconnected) so
|
||||
progress accordingly */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int keyspan_pda_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
{
|
||||
@ -682,13 +711,6 @@ static int keyspan_pda_open(struct tty_struct *tty,
|
||||
priv->tx_room = room;
|
||||
priv->tx_throttled = room ? 0 : 1;
|
||||
|
||||
/* the normal serial device seems to always turn on DTR and RTS here,
|
||||
so do the same */
|
||||
if (tty && (tty->termios->c_cflag & CBAUD))
|
||||
keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2));
|
||||
else
|
||||
keyspan_pda_set_modem_info(serial, 0);
|
||||
|
||||
/*Start reading from the device*/
|
||||
port->interrupt_in_urb->dev = serial->dev;
|
||||
rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
|
||||
@ -700,19 +722,11 @@ static int keyspan_pda_open(struct tty_struct *tty,
|
||||
error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static void keyspan_pda_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void keyspan_pda_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
|
||||
if (serial->dev) {
|
||||
/* the normal serial device seems to always shut
|
||||
off DTR and RTS now */
|
||||
if (tty->termios->c_cflag & HUPCL)
|
||||
keyspan_pda_set_modem_info(serial, 0);
|
||||
|
||||
/* shutdown our bulk reads and writes */
|
||||
usb_kill_urb(port->write_urb);
|
||||
usb_kill_urb(port->interrupt_in_urb);
|
||||
@ -839,6 +853,8 @@ static struct usb_serial_driver keyspan_pda_device = {
|
||||
.usb_driver = &keyspan_pda_driver,
|
||||
.id_table = id_table_std,
|
||||
.num_ports = 1,
|
||||
.dtr_rts = keyspan_pda_dtr_rts,
|
||||
.carrier_raised = keyspan_pda_carrier_raised,
|
||||
.open = keyspan_pda_open,
|
||||
.close = keyspan_pda_close,
|
||||
.write = keyspan_pda_write,
|
||||
|
@ -76,8 +76,7 @@ static int klsi_105_startup(struct usb_serial *serial);
|
||||
static void klsi_105_shutdown(struct usb_serial *serial);
|
||||
static int klsi_105_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void klsi_105_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void klsi_105_close(struct usb_serial_port *port);
|
||||
static int klsi_105_write(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, const unsigned char *buf, int count);
|
||||
static void klsi_105_write_bulk_callback(struct urb *urb);
|
||||
@ -447,8 +446,7 @@ exit:
|
||||
} /* klsi_105_open */
|
||||
|
||||
|
||||
static void klsi_105_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void klsi_105_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct klsi_105_private *priv = usb_get_serial_port_data(port);
|
||||
int rc;
|
||||
|
@ -72,8 +72,7 @@ static int kobil_startup(struct usb_serial *serial);
|
||||
static void kobil_shutdown(struct usb_serial *serial);
|
||||
static int kobil_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void kobil_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
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);
|
||||
@ -209,7 +208,7 @@ static void kobil_shutdown(struct usb_serial *serial)
|
||||
|
||||
for (i = 0; i < serial->num_ports; ++i) {
|
||||
while (serial->port[i]->port.count > 0)
|
||||
kobil_close(NULL, serial->port[i], NULL);
|
||||
kobil_close(serial->port[i]);
|
||||
kfree(usb_get_serial_port_data(serial->port[i]));
|
||||
usb_set_serial_port_data(serial->port[i], NULL);
|
||||
}
|
||||
@ -346,11 +345,11 @@ static int kobil_open(struct tty_struct *tty,
|
||||
}
|
||||
|
||||
|
||||
static void kobil_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void kobil_close(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
/* FIXME: Add rts/dtr methods */
|
||||
if (port->write_urb) {
|
||||
usb_kill_urb(port->write_urb);
|
||||
usb_free_urb(port->write_urb);
|
||||
|
@ -95,8 +95,8 @@ static int mct_u232_startup(struct usb_serial *serial);
|
||||
static void mct_u232_shutdown(struct usb_serial *serial);
|
||||
static int mct_u232_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void mct_u232_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
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);
|
||||
@ -140,6 +140,7 @@ static struct usb_serial_driver mct_u232_device = {
|
||||
.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,
|
||||
@ -496,29 +497,29 @@ error:
|
||||
return retval;
|
||||
} /* mct_u232_open */
|
||||
|
||||
|
||||
static void mct_u232_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
|
||||
{
|
||||
unsigned int c_cflag;
|
||||
unsigned int control_state;
|
||||
struct mct_u232_private *priv = usb_get_serial_port_data(port);
|
||||
dbg("%s port %d", __func__, port->number);
|
||||
|
||||
if (tty) {
|
||||
c_cflag = tty->termios->c_cflag;
|
||||
mutex_lock(&port->serial->disc_mutex);
|
||||
if (c_cflag & HUPCL && !port->serial->disconnected) {
|
||||
/* drop DTR and RTS */
|
||||
spin_lock_irq(&priv->lock);
|
||||
mutex_lock(&port->serial->disc_mutex);
|
||||
if (!port->serial->disconnected) {
|
||||
/* drop DTR and RTS */
|
||||
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->serial, control_state);
|
||||
}
|
||||
mutex_unlock(&port->serial->disc_mutex);
|
||||
control_state = priv->control_state;
|
||||
spin_unlock_irq(&priv->lock);
|
||||
mct_u232_set_modem_ctrl(port->serial, control_state);
|
||||
}
|
||||
mutex_unlock(&port->serial->disc_mutex);
|
||||
}
|
||||
|
||||
static void mct_u232_close(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s port %d", __func__, port->number);
|
||||
|
||||
if (port->serial->dev) {
|
||||
/* shutdown our urbs */
|
||||
|
@ -533,8 +533,7 @@ static int mos7720_chars_in_buffer(struct tty_struct *tty)
|
||||
return chars;
|
||||
}
|
||||
|
||||
static void mos7720_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void mos7720_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial;
|
||||
struct moschip_port *mos7720_port;
|
||||
|
@ -1135,54 +1135,12 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty)
|
||||
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
*
|
||||
* mos7840_block_until_tx_empty
|
||||
*
|
||||
* This function will block the close until one of the following:
|
||||
* 1. TX count are 0
|
||||
* 2. The mos7840 has stopped
|
||||
* 3. A timeout of 3 seconds without activity has expired
|
||||
*
|
||||
************************************************************************/
|
||||
static void mos7840_block_until_tx_empty(struct tty_struct *tty,
|
||||
struct moschip_port *mos7840_port)
|
||||
{
|
||||
int timeout = HZ / 10;
|
||||
int wait = 30;
|
||||
int count;
|
||||
|
||||
while (1) {
|
||||
|
||||
count = mos7840_chars_in_buffer(tty);
|
||||
|
||||
/* Check for Buffer status */
|
||||
if (count <= 0)
|
||||
return;
|
||||
|
||||
/* Block the thread for a while */
|
||||
interruptible_sleep_on_timeout(&mos7840_port->wait_chase,
|
||||
timeout);
|
||||
|
||||
/* No activity.. count down section */
|
||||
wait--;
|
||||
if (wait == 0) {
|
||||
dbg("%s - TIMEOUT", __func__);
|
||||
return;
|
||||
} else {
|
||||
/* Reset timeout value back to seconds */
|
||||
wait = 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* mos7840_close
|
||||
* this function is called by the tty driver when a port is closed
|
||||
*****************************************************************************/
|
||||
|
||||
static void mos7840_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void mos7840_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial;
|
||||
struct moschip_port *mos7840_port;
|
||||
@ -1223,10 +1181,6 @@ static void mos7840_close(struct tty_struct *tty,
|
||||
}
|
||||
}
|
||||
|
||||
if (serial->dev)
|
||||
/* flush and block until tx is empty */
|
||||
mos7840_block_until_tx_empty(tty, mos7840_port);
|
||||
|
||||
/* While closing port, shutdown all bulk read, write *
|
||||
* and interrupt read if they exists */
|
||||
if (serial->dev) {
|
||||
|
@ -98,8 +98,7 @@ static int navman_open(struct tty_struct *tty,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void navman_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void navman_close(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
|
@ -66,8 +66,7 @@ static int debug;
|
||||
/* function prototypes */
|
||||
static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void omninet_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void omninet_close(struct usb_serial_port *port);
|
||||
static void omninet_read_bulk_callback(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,
|
||||
@ -189,8 +188,7 @@ static int omninet_open(struct tty_struct *tty,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void omninet_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void omninet_close(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
usb_kill_urb(port->read_urb);
|
||||
|
@ -173,8 +173,7 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void opticon_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp)
|
||||
static void opticon_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct opticon_private *priv = usb_get_serial_data(port->serial);
|
||||
|
||||
|
@ -45,8 +45,9 @@
|
||||
/* Function prototypes */
|
||||
static int option_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void option_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void option_close(struct usb_serial_port *port);
|
||||
static void option_dtr_rts(struct usb_serial_port *port, int on);
|
||||
|
||||
static int option_startup(struct usb_serial *serial);
|
||||
static void option_shutdown(struct usb_serial *serial);
|
||||
static int option_write_room(struct tty_struct *tty);
|
||||
@ -61,7 +62,7 @@ static void option_set_termios(struct tty_struct *tty,
|
||||
static int option_tiocmget(struct tty_struct *tty, struct file *file);
|
||||
static int option_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
unsigned int set, unsigned int clear);
|
||||
static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *port);
|
||||
static int option_send_setup(struct usb_serial_port *port);
|
||||
static int option_suspend(struct usb_serial *serial, pm_message_t message);
|
||||
static int option_resume(struct usb_serial *serial);
|
||||
|
||||
@ -551,6 +552,7 @@ static struct usb_serial_driver option_1port_device = {
|
||||
.num_ports = 1,
|
||||
.open = option_open,
|
||||
.close = option_close,
|
||||
.dtr_rts = option_dtr_rts,
|
||||
.write = option_write,
|
||||
.write_room = option_write_room,
|
||||
.chars_in_buffer = option_chars_in_buffer,
|
||||
@ -630,7 +632,7 @@ static void option_set_termios(struct tty_struct *tty,
|
||||
dbg("%s", __func__);
|
||||
/* Doesn't support option setting */
|
||||
tty_termios_copy_hw(tty->termios, old_termios);
|
||||
option_send_setup(tty, port);
|
||||
option_send_setup(port);
|
||||
}
|
||||
|
||||
static int option_tiocmget(struct tty_struct *tty, struct file *file)
|
||||
@ -669,7 +671,7 @@ static int option_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
portdata->rts_state = 0;
|
||||
if (clear & TIOCM_DTR)
|
||||
portdata->dtr_state = 0;
|
||||
return option_send_setup(tty, port);
|
||||
return option_send_setup(port);
|
||||
}
|
||||
|
||||
/* Write */
|
||||
@ -897,10 +899,6 @@ static int option_open(struct tty_struct *tty,
|
||||
|
||||
dbg("%s", __func__);
|
||||
|
||||
/* Set some sane defaults */
|
||||
portdata->rts_state = 1;
|
||||
portdata->dtr_state = 1;
|
||||
|
||||
/* Reset low level data toggle and start reading from endpoints */
|
||||
for (i = 0; i < N_IN_URB; i++) {
|
||||
urb = portdata->in_urbs[i];
|
||||
@ -936,13 +934,28 @@ static int option_open(struct tty_struct *tty,
|
||||
usb_pipeout(urb->pipe), 0); */
|
||||
}
|
||||
|
||||
option_send_setup(tty, port);
|
||||
option_send_setup(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void option_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void option_dtr_rts(struct usb_serial_port *port, int on)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct option_port_private *portdata;
|
||||
|
||||
dbg("%s", __func__);
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
mutex_lock(&serial->disc_mutex);
|
||||
portdata->rts_state = on;
|
||||
portdata->dtr_state = on;
|
||||
if (serial->dev)
|
||||
option_send_setup(port);
|
||||
mutex_unlock(&serial->disc_mutex);
|
||||
}
|
||||
|
||||
|
||||
static void option_close(struct usb_serial_port *port)
|
||||
{
|
||||
int i;
|
||||
struct usb_serial *serial = port->serial;
|
||||
@ -951,22 +964,13 @@ static void option_close(struct tty_struct *tty,
|
||||
dbg("%s", __func__);
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
portdata->rts_state = 0;
|
||||
portdata->dtr_state = 0;
|
||||
|
||||
if (serial->dev) {
|
||||
mutex_lock(&serial->disc_mutex);
|
||||
if (!serial->disconnected)
|
||||
option_send_setup(tty, port);
|
||||
mutex_unlock(&serial->disc_mutex);
|
||||
|
||||
/* Stop reading/writing urbs */
|
||||
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]);
|
||||
}
|
||||
tty_port_tty_set(&port->port, NULL);
|
||||
}
|
||||
|
||||
/* Helper functions used by option_setup_urbs */
|
||||
@ -1032,28 +1036,24 @@ static void option_setup_urbs(struct usb_serial *serial)
|
||||
* This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN
|
||||
* CDC.
|
||||
*/
|
||||
static int option_send_setup(struct tty_struct *tty,
|
||||
struct usb_serial_port *port)
|
||||
static int option_send_setup(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct option_port_private *portdata;
|
||||
int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
|
||||
int val = 0;
|
||||
dbg("%s", __func__);
|
||||
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
if (tty) {
|
||||
int val = 0;
|
||||
if (portdata->dtr_state)
|
||||
val |= 0x01;
|
||||
if (portdata->rts_state)
|
||||
val |= 0x02;
|
||||
if (portdata->dtr_state)
|
||||
val |= 0x01;
|
||||
if (portdata->rts_state)
|
||||
val |= 0x02;
|
||||
|
||||
return usb_control_msg(serial->dev,
|
||||
usb_rcvctrlpipe(serial->dev, 0),
|
||||
0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
return 0;
|
||||
return usb_control_msg(serial->dev,
|
||||
usb_rcvctrlpipe(serial->dev, 0),
|
||||
0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int option_startup(struct usb_serial *serial)
|
||||
|
@ -143,8 +143,7 @@ struct oti6858_control_pkt {
|
||||
/* function prototypes */
|
||||
static int oti6858_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void oti6858_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
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 int oti6858_ioctl(struct tty_struct *tty, struct file *file,
|
||||
@ -622,67 +621,30 @@ static int oti6858_open(struct tty_struct *tty,
|
||||
if (result != 0) {
|
||||
dev_err(&port->dev, "%s(): usb_submit_urb() failed"
|
||||
" with error %d\n", __func__, result);
|
||||
oti6858_close(tty, port, NULL);
|
||||
oti6858_close(port);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
/* setup termios */
|
||||
if (tty)
|
||||
oti6858_set_termios(tty, port, &tmp_termios);
|
||||
|
||||
port->port.drain_delay = 256; /* FIXME: check the FIFO length */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void oti6858_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void oti6858_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct oti6858_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned long flags;
|
||||
long timeout;
|
||||
wait_queue_t wait;
|
||||
|
||||
dbg("%s(port = %d)", __func__, port->number);
|
||||
|
||||
/* wait for data to drain from the buffer */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
timeout = 30 * HZ; /* PL2303_CLOSING_WAIT */
|
||||
init_waitqueue_entry(&wait, current);
|
||||
add_wait_queue(&tty->write_wait, &wait);
|
||||
dbg("%s(): entering wait loop", __func__);
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (oti6858_buf_data_avail(priv->buf) == 0
|
||||
|| timeout == 0 || signal_pending(current)
|
||||
|| port->serial->disconnected)
|
||||
break;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
timeout = schedule_timeout(timeout);
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&tty->write_wait, &wait);
|
||||
dbg("%s(): after wait loop", __func__);
|
||||
|
||||
/* clear out any remaining data in the buffer */
|
||||
oti6858_buf_clear(priv->buf);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
/* wait for characters to drain from the device */
|
||||
/* (this is long enough for the entire 256 byte */
|
||||
/* pl2303 hardware buffer to drain with no flow */
|
||||
/* control for data rates of 1200 bps or more, */
|
||||
/* for lower rates we should really know how much */
|
||||
/* data is in the buffer to compute a delay */
|
||||
/* that is not unnecessarily long) */
|
||||
/* FIXME
|
||||
bps = tty_get_baud_rate(tty);
|
||||
if (bps > 1200)
|
||||
timeout = max((HZ*2560)/bps,HZ/10);
|
||||
else
|
||||
*/
|
||||
timeout = 2*HZ;
|
||||
schedule_timeout_interruptible(timeout);
|
||||
dbg("%s(): after schedule_timeout_interruptible()", __func__);
|
||||
dbg("%s(): after buf_clear()", __func__);
|
||||
|
||||
/* cancel scheduled setup */
|
||||
cancel_delayed_work(&priv->delayed_setup_work);
|
||||
@ -694,15 +656,6 @@ static void oti6858_close(struct tty_struct *tty,
|
||||
usb_kill_urb(port->write_urb);
|
||||
usb_kill_urb(port->read_urb);
|
||||
usb_kill_urb(port->interrupt_in_urb);
|
||||
|
||||
/*
|
||||
if (tty && (tty->termios->c_cflag) & HUPCL) {
|
||||
// drop DTR and RTS
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->pending_setup.control &= ~CONTROL_MASK;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static int oti6858_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
|
@ -652,69 +652,41 @@ static void pl2303_set_termios(struct tty_struct *tty,
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
static void pl2303_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
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);
|
||||
/* 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 void pl2303_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct pl2303_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned long flags;
|
||||
unsigned int c_cflag;
|
||||
int bps;
|
||||
long timeout;
|
||||
wait_queue_t wait;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
/* wait for data to drain from the buffer */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
timeout = PL2303_CLOSING_WAIT;
|
||||
init_waitqueue_entry(&wait, current);
|
||||
add_wait_queue(&tty->write_wait, &wait);
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (pl2303_buf_data_avail(priv->buf) == 0 ||
|
||||
timeout == 0 || signal_pending(current) ||
|
||||
port->serial->disconnected)
|
||||
break;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
timeout = schedule_timeout(timeout);
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&tty->write_wait, &wait);
|
||||
/* clear out any remaining data in the buffer */
|
||||
pl2303_buf_clear(priv->buf);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
/* wait for characters to drain from the device */
|
||||
/* (this is long enough for the entire 256 byte */
|
||||
/* pl2303 hardware buffer to drain with no flow */
|
||||
/* control for data rates of 1200 bps or more, */
|
||||
/* for lower rates we should really know how much */
|
||||
/* data is in the buffer to compute a delay */
|
||||
/* that is not unnecessarily long) */
|
||||
bps = tty_get_baud_rate(tty);
|
||||
if (bps > 1200)
|
||||
timeout = max((HZ*2560)/bps, HZ/10);
|
||||
else
|
||||
timeout = 2*HZ;
|
||||
schedule_timeout_interruptible(timeout);
|
||||
|
||||
/* shutdown our urbs */
|
||||
dbg("%s - shutting down urbs", __func__);
|
||||
usb_kill_urb(port->write_urb);
|
||||
usb_kill_urb(port->read_urb);
|
||||
usb_kill_urb(port->interrupt_in_urb);
|
||||
|
||||
if (tty) {
|
||||
c_cflag = tty->termios->c_cflag;
|
||||
if (c_cflag & HUPCL) {
|
||||
/* drop DTR and RTS */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->line_control = 0;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
set_control_lines(port->serial->dev, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int pl2303_open(struct tty_struct *tty,
|
||||
@ -748,7 +720,7 @@ static int pl2303_open(struct tty_struct *tty,
|
||||
if (result) {
|
||||
dev_err(&port->dev, "%s - failed submitting read urb,"
|
||||
" error %d\n", __func__, result);
|
||||
pl2303_close(tty, port, NULL);
|
||||
pl2303_close(port);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
@ -758,9 +730,10 @@ static int pl2303_open(struct tty_struct *tty,
|
||||
if (result) {
|
||||
dev_err(&port->dev, "%s - failed submitting interrupt urb,"
|
||||
" error %d\n", __func__, result);
|
||||
pl2303_close(tty, port, NULL);
|
||||
pl2303_close(port);
|
||||
return -EPROTO;
|
||||
}
|
||||
port->port.drain_delay = 256;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -821,6 +794,14 @@ static int pl2303_tiocmget(struct tty_struct *tty, struct file *file)
|
||||
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 wait_modem_info(struct usb_serial_port *port, unsigned int arg)
|
||||
{
|
||||
struct pl2303_private *priv = usb_get_serial_port_data(port);
|
||||
@ -1125,6 +1106,8 @@ static struct usb_serial_driver pl2303_device = {
|
||||
.num_ports = 1,
|
||||
.open = pl2303_open,
|
||||
.close = pl2303_close,
|
||||
.dtr_rts = pl2303_dtr_rts,
|
||||
.carrier_raised = pl2303_carrier_raised,
|
||||
.write = pl2303_write,
|
||||
.ioctl = pl2303_ioctl,
|
||||
.break_ctl = pl2303_break_ctl,
|
||||
|
@ -26,12 +26,10 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/serial.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
#define SWIMS_USB_REQUEST_SetPower 0x00
|
||||
#define SWIMS_USB_REQUEST_SetNmea 0x07
|
||||
|
||||
/* per port private data */
|
||||
#define N_IN_URB 4
|
||||
#define N_OUT_URB 4
|
||||
#define IN_BUFLEN 4096
|
||||
@ -39,6 +37,12 @@
|
||||
static int debug;
|
||||
static int nmea;
|
||||
|
||||
/* Used in interface blacklisting */
|
||||
struct sierra_iface_info {
|
||||
const u32 infolen; /* number of interface numbers on blacklist */
|
||||
const u8 *ifaceinfo; /* pointer to the array holding the numbers */
|
||||
};
|
||||
|
||||
static int sierra_set_power_state(struct usb_device *udev, __u16 swiState)
|
||||
{
|
||||
int result;
|
||||
@ -85,6 +89,23 @@ static int sierra_calc_num_ports(struct usb_serial *serial)
|
||||
return result;
|
||||
}
|
||||
|
||||
static int is_blacklisted(const u8 ifnum,
|
||||
const struct sierra_iface_info *blacklist)
|
||||
{
|
||||
const u8 *info;
|
||||
int i;
|
||||
|
||||
if (blacklist) {
|
||||
info = blacklist->ifaceinfo;
|
||||
|
||||
for (i = 0; i < blacklist->infolen; i++) {
|
||||
if (info[i] == ifnum)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sierra_calc_interface(struct usb_serial *serial)
|
||||
{
|
||||
int interface;
|
||||
@ -153,9 +174,25 @@ static int sierra_probe(struct usb_serial *serial,
|
||||
*/
|
||||
usb_set_serial_data(serial, (void *)num_ports);
|
||||
|
||||
/* ifnum could have changed - by calling usb_set_interface */
|
||||
ifnum = sierra_calc_interface(serial);
|
||||
|
||||
if (is_blacklisted(ifnum,
|
||||
(struct sierra_iface_info *)id->driver_info)) {
|
||||
dev_dbg(&serial->dev->dev,
|
||||
"Ignoring blacklisted interface #%d\n", ifnum);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11 };
|
||||
static const struct sierra_iface_info direct_ip_interface_blacklist = {
|
||||
.infolen = ARRAY_SIZE(direct_ip_non_serial_ifaces),
|
||||
.ifaceinfo = direct_ip_non_serial_ifaces,
|
||||
};
|
||||
|
||||
static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */
|
||||
{ USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */
|
||||
@ -188,9 +225,11 @@ static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781 */
|
||||
{ USB_DEVICE(0x1199, 0x683A) }, /* Sierra Wireless MC8785 */
|
||||
{ USB_DEVICE(0x1199, 0x683B) }, /* Sierra Wireless MC8785 Composite */
|
||||
{ USB_DEVICE(0x1199, 0x683C) }, /* Sierra Wireless MC8790 */
|
||||
{ USB_DEVICE(0x1199, 0x683D) }, /* Sierra Wireless MC8790 */
|
||||
{ USB_DEVICE(0x1199, 0x683E) }, /* Sierra Wireless MC8790 */
|
||||
/* Sierra Wireless MC8790, MC8791, MC8792 Composite */
|
||||
{ USB_DEVICE(0x1199, 0x683C) },
|
||||
{ USB_DEVICE(0x1199, 0x683D) }, /* Sierra Wireless MC8791 Composite */
|
||||
/* Sierra Wireless MC8790, MC8791, MC8792 */
|
||||
{ USB_DEVICE(0x1199, 0x683E) },
|
||||
{ USB_DEVICE(0x1199, 0x6850) }, /* Sierra Wireless AirCard 880 */
|
||||
{ USB_DEVICE(0x1199, 0x6851) }, /* Sierra Wireless AirCard 881 */
|
||||
{ USB_DEVICE(0x1199, 0x6852) }, /* Sierra Wireless AirCard 880 E */
|
||||
@ -211,6 +250,10 @@ static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */
|
||||
{ USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */
|
||||
|
||||
{ USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */
|
||||
.driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
|
||||
},
|
||||
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
@ -229,7 +272,6 @@ struct sierra_port_private {
|
||||
|
||||
/* Input endpoints and buffers for this port */
|
||||
struct urb *in_urbs[N_IN_URB];
|
||||
char *in_buffer[N_IN_URB];
|
||||
|
||||
/* Settings for the port */
|
||||
int rts_state; /* Handshaking pins (outputs) */
|
||||
@ -240,57 +282,50 @@ struct sierra_port_private {
|
||||
int ri_state;
|
||||
};
|
||||
|
||||
static int sierra_send_setup(struct tty_struct *tty,
|
||||
struct usb_serial_port *port)
|
||||
static int sierra_send_setup(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct sierra_port_private *portdata;
|
||||
__u16 interface = 0;
|
||||
int val = 0;
|
||||
|
||||
dev_dbg(&port->dev, "%s", __func__);
|
||||
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
if (tty) {
|
||||
int val = 0;
|
||||
if (portdata->dtr_state)
|
||||
val |= 0x01;
|
||||
if (portdata->rts_state)
|
||||
val |= 0x02;
|
||||
|
||||
/* If composite device then properly report interface */
|
||||
if (serial->num_ports == 1) {
|
||||
interface = sierra_calc_interface(serial);
|
||||
|
||||
/* Control message is sent only to interfaces with
|
||||
* interrupt_in endpoints
|
||||
*/
|
||||
if (port->interrupt_in_urb) {
|
||||
/* send control message */
|
||||
return usb_control_msg(serial->dev,
|
||||
usb_rcvctrlpipe(serial->dev, 0),
|
||||
0x22, 0x21, val, interface,
|
||||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise the need to do non-composite mapping */
|
||||
else {
|
||||
if (port->bulk_out_endpointAddress == 2)
|
||||
interface = 0;
|
||||
else if (port->bulk_out_endpointAddress == 4)
|
||||
interface = 1;
|
||||
else if (port->bulk_out_endpointAddress == 5)
|
||||
interface = 2;
|
||||
if (portdata->dtr_state)
|
||||
val |= 0x01;
|
||||
if (portdata->rts_state)
|
||||
val |= 0x02;
|
||||
|
||||
/* If composite device then properly report interface */
|
||||
if (serial->num_ports == 1) {
|
||||
interface = sierra_calc_interface(serial);
|
||||
/* Control message is sent only to interfaces with
|
||||
* interrupt_in endpoints
|
||||
*/
|
||||
if (port->interrupt_in_urb) {
|
||||
/* send control message */
|
||||
return usb_control_msg(serial->dev,
|
||||
usb_rcvctrlpipe(serial->dev, 0),
|
||||
0x22, 0x21, val, interface,
|
||||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise the need to do non-composite mapping */
|
||||
else {
|
||||
if (port->bulk_out_endpointAddress == 2)
|
||||
interface = 0;
|
||||
else if (port->bulk_out_endpointAddress == 4)
|
||||
interface = 1;
|
||||
else if (port->bulk_out_endpointAddress == 5)
|
||||
interface = 2;
|
||||
return usb_control_msg(serial->dev,
|
||||
usb_rcvctrlpipe(serial->dev, 0),
|
||||
0x22, 0x21, val, interface,
|
||||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -299,7 +334,7 @@ static void sierra_set_termios(struct tty_struct *tty,
|
||||
{
|
||||
dev_dbg(&port->dev, "%s", __func__);
|
||||
tty_termios_copy_hw(tty->termios, old_termios);
|
||||
sierra_send_setup(tty, port);
|
||||
sierra_send_setup(port);
|
||||
}
|
||||
|
||||
static int sierra_tiocmget(struct tty_struct *tty, struct file *file)
|
||||
@ -338,7 +373,18 @@ static int sierra_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
portdata->rts_state = 0;
|
||||
if (clear & TIOCM_DTR)
|
||||
portdata->dtr_state = 0;
|
||||
return sierra_send_setup(tty, port);
|
||||
return sierra_send_setup(port);
|
||||
}
|
||||
|
||||
static void sierra_release_urb(struct urb *urb)
|
||||
{
|
||||
struct usb_serial_port *port;
|
||||
if (urb) {
|
||||
port = urb->context;
|
||||
dev_dbg(&port->dev, "%s: %p\n", __func__, urb);
|
||||
kfree(urb->transfer_buffer);
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
}
|
||||
|
||||
static void sierra_outdat_callback(struct urb *urb)
|
||||
@ -465,7 +511,7 @@ static void sierra_indat_callback(struct urb *urb)
|
||||
" received", __func__);
|
||||
|
||||
/* Resubmit urb so we continue receiving */
|
||||
if (port->port.count && status != -ESHUTDOWN) {
|
||||
if (port->port.count && status != -ESHUTDOWN && status != -EPERM) {
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (err)
|
||||
dev_err(&port->dev, "resubmit read urb failed."
|
||||
@ -557,14 +603,129 @@ static int sierra_write_room(struct tty_struct *tty)
|
||||
return 2048;
|
||||
}
|
||||
|
||||
static void sierra_stop_rx_urbs(struct usb_serial_port *port)
|
||||
{
|
||||
int i;
|
||||
struct sierra_port_private *portdata = usb_get_serial_port_data(port);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++)
|
||||
usb_kill_urb(portdata->in_urbs[i]);
|
||||
|
||||
usb_kill_urb(port->interrupt_in_urb);
|
||||
}
|
||||
|
||||
static int sierra_submit_rx_urbs(struct usb_serial_port *port, gfp_t mem_flags)
|
||||
{
|
||||
int ok_cnt;
|
||||
int err = -EINVAL;
|
||||
int i;
|
||||
struct urb *urb;
|
||||
struct sierra_port_private *portdata = usb_get_serial_port_data(port);
|
||||
|
||||
ok_cnt = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) {
|
||||
urb = portdata->in_urbs[i];
|
||||
if (!urb)
|
||||
continue;
|
||||
err = usb_submit_urb(urb, mem_flags);
|
||||
if (err) {
|
||||
dev_err(&port->dev, "%s: submit urb failed: %d\n",
|
||||
__func__, err);
|
||||
} else {
|
||||
ok_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok_cnt && port->interrupt_in_urb) {
|
||||
err = usb_submit_urb(port->interrupt_in_urb, mem_flags);
|
||||
if (err) {
|
||||
dev_err(&port->dev, "%s: submit intr urb failed: %d\n",
|
||||
__func__, err);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok_cnt > 0) /* at least one rx urb submitted */
|
||||
return 0;
|
||||
else
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint,
|
||||
int dir, void *ctx, int len,
|
||||
gfp_t mem_flags,
|
||||
usb_complete_t callback)
|
||||
{
|
||||
struct urb *urb;
|
||||
u8 *buf;
|
||||
|
||||
if (endpoint == -1)
|
||||
return NULL;
|
||||
|
||||
urb = usb_alloc_urb(0, mem_flags);
|
||||
if (urb == NULL) {
|
||||
dev_dbg(&serial->dev->dev, "%s: alloc for endpoint %d failed\n",
|
||||
__func__, endpoint);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = kmalloc(len, mem_flags);
|
||||
if (buf) {
|
||||
/* Fill URB using supplied data */
|
||||
usb_fill_bulk_urb(urb, serial->dev,
|
||||
usb_sndbulkpipe(serial->dev, endpoint) | dir,
|
||||
buf, len, callback, ctx);
|
||||
|
||||
/* debug */
|
||||
dev_dbg(&serial->dev->dev, "%s %c u : %p d:%p\n", __func__,
|
||||
dir == USB_DIR_IN ? 'i' : 'o', urb, buf);
|
||||
} else {
|
||||
dev_dbg(&serial->dev->dev, "%s %c u:%p d:%p\n", __func__,
|
||||
dir == USB_DIR_IN ? 'i' : 'o', urb, buf);
|
||||
|
||||
sierra_release_urb(urb);
|
||||
urb = NULL;
|
||||
}
|
||||
|
||||
return urb;
|
||||
}
|
||||
|
||||
static void sierra_close(struct usb_serial_port *port)
|
||||
{
|
||||
int i;
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct sierra_port_private *portdata;
|
||||
|
||||
dev_dbg(&port->dev, "%s\n", __func__);
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
portdata->rts_state = 0;
|
||||
portdata->dtr_state = 0;
|
||||
|
||||
if (serial->dev) {
|
||||
mutex_lock(&serial->disc_mutex);
|
||||
if (!serial->disconnected)
|
||||
sierra_send_setup(port);
|
||||
mutex_unlock(&serial->disc_mutex);
|
||||
|
||||
/* Stop reading urbs */
|
||||
sierra_stop_rx_urbs(port);
|
||||
/* .. and release them */
|
||||
for (i = 0; i < N_IN_URB; i++) {
|
||||
sierra_release_urb(portdata->in_urbs[i]);
|
||||
portdata->in_urbs[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int sierra_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
{
|
||||
struct sierra_port_private *portdata;
|
||||
struct usb_serial *serial = port->serial;
|
||||
int i;
|
||||
int err;
|
||||
int endpoint;
|
||||
struct urb *urb;
|
||||
int result;
|
||||
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
@ -574,77 +735,52 @@ static int sierra_open(struct tty_struct *tty,
|
||||
portdata->rts_state = 1;
|
||||
portdata->dtr_state = 1;
|
||||
|
||||
/* Reset low level data toggle and start reading from endpoints */
|
||||
for (i = 0; i < N_IN_URB; i++) {
|
||||
urb = portdata->in_urbs[i];
|
||||
if (!urb)
|
||||
continue;
|
||||
if (urb->dev != serial->dev) {
|
||||
dev_dbg(&port->dev, "%s: dev %p != %p",
|
||||
__func__, urb->dev, serial->dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* make sure endpoint data toggle is synchronized with the
|
||||
* device
|
||||
*/
|
||||
usb_clear_halt(urb->dev, urb->pipe);
|
||||
|
||||
result = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (result) {
|
||||
dev_err(&port->dev, "submit urb %d failed (%d) %d\n",
|
||||
i, result, urb->transfer_buffer_length);
|
||||
}
|
||||
endpoint = port->bulk_in_endpointAddress;
|
||||
for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) {
|
||||
urb = sierra_setup_urb(serial, endpoint, USB_DIR_IN, port,
|
||||
IN_BUFLEN, GFP_KERNEL,
|
||||
sierra_indat_callback);
|
||||
portdata->in_urbs[i] = urb;
|
||||
}
|
||||
/* clear halt condition */
|
||||
usb_clear_halt(serial->dev,
|
||||
usb_sndbulkpipe(serial->dev, endpoint) | USB_DIR_IN);
|
||||
|
||||
sierra_send_setup(tty, port);
|
||||
|
||||
/* start up the interrupt endpoint if we have one */
|
||||
if (port->interrupt_in_urb) {
|
||||
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
|
||||
if (result)
|
||||
dev_err(&port->dev, "submit irq_in urb failed %d\n",
|
||||
result);
|
||||
err = sierra_submit_rx_urbs(port, GFP_KERNEL);
|
||||
if (err) {
|
||||
/* get rid of everything as in close */
|
||||
sierra_close(port);
|
||||
return err;
|
||||
}
|
||||
sierra_send_setup(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sierra_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
|
||||
static void sierra_dtr_rts(struct usb_serial_port *port, int on)
|
||||
{
|
||||
int i;
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct sierra_port_private *portdata;
|
||||
|
||||
dev_dbg(&port->dev, "%s", __func__);
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
portdata->rts_state = 0;
|
||||
portdata->dtr_state = 0;
|
||||
portdata->rts_state = on;
|
||||
portdata->dtr_state = on;
|
||||
|
||||
if (serial->dev) {
|
||||
mutex_lock(&serial->disc_mutex);
|
||||
if (!serial->disconnected)
|
||||
sierra_send_setup(tty, port);
|
||||
sierra_send_setup(port);
|
||||
mutex_unlock(&serial->disc_mutex);
|
||||
|
||||
/* Stop reading/writing urbs */
|
||||
for (i = 0; i < N_IN_URB; i++)
|
||||
usb_kill_urb(portdata->in_urbs[i]);
|
||||
}
|
||||
|
||||
usb_kill_urb(port->interrupt_in_urb);
|
||||
tty_port_tty_set(&port->port, NULL);
|
||||
}
|
||||
|
||||
static int sierra_startup(struct usb_serial *serial)
|
||||
{
|
||||
struct usb_serial_port *port;
|
||||
struct sierra_port_private *portdata;
|
||||
struct urb *urb;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
dev_dbg(&serial->dev->dev, "%s", __func__);
|
||||
|
||||
@ -666,34 +802,8 @@ static int sierra_startup(struct usb_serial *serial)
|
||||
return -ENOMEM;
|
||||
}
|
||||
spin_lock_init(&portdata->lock);
|
||||
for (j = 0; j < N_IN_URB; j++) {
|
||||
portdata->in_buffer[j] = kmalloc(IN_BUFLEN, GFP_KERNEL);
|
||||
if (!portdata->in_buffer[j]) {
|
||||
for (--j; j >= 0; j--)
|
||||
kfree(portdata->in_buffer[j]);
|
||||
kfree(portdata);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the port private data pointer */
|
||||
usb_set_serial_port_data(port, portdata);
|
||||
|
||||
/* initialize the in urbs */
|
||||
for (j = 0; j < N_IN_URB; ++j) {
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (urb == NULL) {
|
||||
dev_dbg(&port->dev, "%s: alloc for in "
|
||||
"port failed.", __func__);
|
||||
continue;
|
||||
}
|
||||
/* Fill URB using supplied data. */
|
||||
usb_fill_bulk_urb(urb, serial->dev,
|
||||
usb_rcvbulkpipe(serial->dev,
|
||||
port->bulk_in_endpointAddress),
|
||||
portdata->in_buffer[j], IN_BUFLEN,
|
||||
sierra_indat_callback, port);
|
||||
portdata->in_urbs[j] = urb;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -701,7 +811,7 @@ static int sierra_startup(struct usb_serial *serial)
|
||||
|
||||
static void sierra_shutdown(struct usb_serial *serial)
|
||||
{
|
||||
int i, j;
|
||||
int i;
|
||||
struct usb_serial_port *port;
|
||||
struct sierra_port_private *portdata;
|
||||
|
||||
@ -714,12 +824,6 @@ static void sierra_shutdown(struct usb_serial *serial)
|
||||
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]);
|
||||
usb_free_urb(portdata->in_urbs[j]);
|
||||
kfree(portdata->in_buffer[j]);
|
||||
}
|
||||
kfree(portdata);
|
||||
usb_set_serial_port_data(port, NULL);
|
||||
}
|
||||
@ -737,6 +841,7 @@ static struct usb_serial_driver sierra_device = {
|
||||
.probe = sierra_probe,
|
||||
.open = sierra_open,
|
||||
.close = sierra_close,
|
||||
.dtr_rts = sierra_dtr_rts,
|
||||
.write = sierra_write,
|
||||
.write_room = sierra_write_room,
|
||||
.set_termios = sierra_set_termios,
|
||||
|
@ -446,66 +446,47 @@ static void spcp8x5_set_workMode(struct usb_device *dev, u16 value,
|
||||
"RTSCTS usb_control_msg(enable flowctrl) = %d\n", ret);
|
||||
}
|
||||
|
||||
/* close the serial port. We should wait for data sending to device 1st and
|
||||
* then kill all urb. */
|
||||
static void spcp8x5_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static int spcp8x5_carrier_raised(struct usb_serial_port *port)
|
||||
{
|
||||
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
|
||||
if (priv->line_status & 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_ctrlLine(port->serial->dev, control , priv->type);
|
||||
}
|
||||
|
||||
/* close the serial port. We should wait for data sending to device 1st and
|
||||
* then kill all urb. */
|
||||
static void spcp8x5_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned long flags;
|
||||
unsigned int c_cflag;
|
||||
int bps;
|
||||
long timeout;
|
||||
wait_queue_t wait;
|
||||
int result;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
/* wait for data to drain from the buffer */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
timeout = SPCP8x5_CLOSING_WAIT;
|
||||
init_waitqueue_entry(&wait, current);
|
||||
add_wait_queue(&tty->write_wait, &wait);
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (ringbuf_avail_data(priv->buf) == 0 ||
|
||||
timeout == 0 || signal_pending(current))
|
||||
break;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
timeout = schedule_timeout(timeout);
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&tty->write_wait, &wait);
|
||||
|
||||
/* clear out any remaining data in the buffer */
|
||||
clear_ringbuf(priv->buf);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
/* wait for characters to drain from the device (this is long enough
|
||||
* for the entire all byte spcp8x5 hardware buffer to drain with no
|
||||
* flow control for data rates of 1200 bps or more, for lower rates we
|
||||
* should really know how much data is in the buffer to compute a delay
|
||||
* that is not unnecessarily long) */
|
||||
bps = tty_get_baud_rate(tty);
|
||||
if (bps > 1200)
|
||||
timeout = max((HZ*2560) / bps, HZ/10);
|
||||
else
|
||||
timeout = 2*HZ;
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(timeout);
|
||||
|
||||
/* clear control lines */
|
||||
if (tty) {
|
||||
c_cflag = tty->termios->c_cflag;
|
||||
if (c_cflag & HUPCL) {
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->line_control = 0;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
spcp8x5_set_ctrlLine(port->serial->dev, 0 , priv->type);
|
||||
}
|
||||
}
|
||||
|
||||
/* kill urb */
|
||||
if (port->write_urb != NULL) {
|
||||
result = usb_unlink_urb(port->write_urb);
|
||||
@ -665,13 +646,6 @@ static int spcp8x5_open(struct tty_struct *tty,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (tty && (tty->termios->c_cflag & CBAUD))
|
||||
priv->line_control = MCR_DTR | MCR_RTS;
|
||||
else
|
||||
priv->line_control = 0;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
spcp8x5_set_ctrlLine(serial->dev, priv->line_control , priv->type);
|
||||
|
||||
/* Setup termios */
|
||||
@ -691,9 +665,10 @@ static int spcp8x5_open(struct tty_struct *tty,
|
||||
port->read_urb->dev = serial->dev;
|
||||
ret = usb_submit_urb(port->read_urb, GFP_KERNEL);
|
||||
if (ret) {
|
||||
spcp8x5_close(tty, port, NULL);
|
||||
spcp8x5_close(port);
|
||||
return -EPROTO;
|
||||
}
|
||||
port->port.drain_delay = 256;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1033,6 +1008,8 @@ static struct usb_serial_driver spcp8x5_device = {
|
||||
.num_ports = 1,
|
||||
.open = spcp8x5_open,
|
||||
.close = spcp8x5_close,
|
||||
.dtr_rts = spcp8x5_dtr_rts,
|
||||
.carrier_raised = spcp8x5_carrier_raised,
|
||||
.write = spcp8x5_write,
|
||||
.set_termios = spcp8x5_set_termios,
|
||||
.ioctl = spcp8x5_ioctl,
|
||||
|
@ -152,8 +152,7 @@ static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void symbol_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp)
|
||||
static void symbol_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct symbol_private *priv = usb_get_serial_data(port->serial);
|
||||
|
||||
|
@ -100,8 +100,7 @@ static int ti_startup(struct usb_serial *serial);
|
||||
static void ti_shutdown(struct usb_serial *serial);
|
||||
static int ti_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *file);
|
||||
static void ti_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *file);
|
||||
static void ti_close(struct usb_serial_port *port);
|
||||
static int ti_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *data, int count);
|
||||
static int ti_write_room(struct tty_struct *tty);
|
||||
@ -647,8 +646,7 @@ release_lock:
|
||||
}
|
||||
|
||||
|
||||
static void ti_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *file)
|
||||
static void ti_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct ti_device *tdev;
|
||||
struct ti_port *tport;
|
||||
|
@ -238,9 +238,11 @@ static int serial_open (struct tty_struct *tty, struct file *filp)
|
||||
goto bailout_interface_put;
|
||||
mutex_unlock(&serial->disc_mutex);
|
||||
}
|
||||
|
||||
mutex_unlock(&port->mutex);
|
||||
return 0;
|
||||
/* Now do the correct tty layer semantics */
|
||||
retval = tty_port_block_til_ready(&port->port, tty, filp);
|
||||
if (retval == 0)
|
||||
return 0;
|
||||
|
||||
bailout_interface_put:
|
||||
usb_autopm_put_interface(serial->interface);
|
||||
@ -259,64 +261,89 @@ bailout_serial_put:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void serial_close(struct tty_struct *tty, struct file *filp)
|
||||
/**
|
||||
* serial_do_down - shut down hardware
|
||||
* @port: port to shut down
|
||||
*
|
||||
* Shut down a USB port unless it is the console. We never shut down the
|
||||
* console hardware as it will always be in use.
|
||||
*
|
||||
* Don't free any resources at this point
|
||||
*/
|
||||
static void serial_do_down(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct usb_serial_driver *drv = port->serial->type;
|
||||
struct usb_serial *serial;
|
||||
struct module *owner;
|
||||
int count;
|
||||
|
||||
if (!port)
|
||||
/* The console is magical, do not hang up the console hardware
|
||||
or there will be tears */
|
||||
if (port->console)
|
||||
return;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
mutex_lock(&port->mutex);
|
||||
serial = port->serial;
|
||||
owner = serial->type->driver.owner;
|
||||
|
||||
if (port->port.count == 0) {
|
||||
mutex_unlock(&port->mutex);
|
||||
return;
|
||||
}
|
||||
if (drv->close)
|
||||
drv->close(port);
|
||||
|
||||
if (port->port.count == 1)
|
||||
/* only call the device specific close if this
|
||||
* port is being closed by the last owner. Ensure we do
|
||||
* this before we drop the port count. The call is protected
|
||||
* by the port mutex
|
||||
*/
|
||||
serial->type->close(tty, port, filp);
|
||||
|
||||
if (port->port.count == (port->console ? 2 : 1)) {
|
||||
struct tty_struct *tty = tty_port_tty_get(&port->port);
|
||||
if (tty) {
|
||||
/* We must do this before we drop the port count to
|
||||
zero. */
|
||||
if (tty->driver_data)
|
||||
tty->driver_data = NULL;
|
||||
tty_port_tty_set(&port->port, NULL);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
}
|
||||
|
||||
--port->port.count;
|
||||
count = port->port.count;
|
||||
mutex_unlock(&port->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* serial_do_free - free resources post close/hangup
|
||||
* @port: port to free up
|
||||
*
|
||||
* Do the resource freeing and refcount dropping for the port. We must
|
||||
* be careful about ordering and we must avoid freeing up the console.
|
||||
*/
|
||||
|
||||
static void serial_do_free(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial;
|
||||
struct module *owner;
|
||||
|
||||
/* The console is magical, do not hang up the console hardware
|
||||
or there will be tears */
|
||||
if (port->console)
|
||||
return;
|
||||
|
||||
serial = port->serial;
|
||||
owner = serial->type->driver.owner;
|
||||
put_device(&port->dev);
|
||||
|
||||
/* Mustn't dereference port any more */
|
||||
if (count == 0) {
|
||||
mutex_lock(&serial->disc_mutex);
|
||||
if (!serial->disconnected)
|
||||
usb_autopm_put_interface(serial->interface);
|
||||
mutex_unlock(&serial->disc_mutex);
|
||||
}
|
||||
mutex_lock(&serial->disc_mutex);
|
||||
if (!serial->disconnected)
|
||||
usb_autopm_put_interface(serial->interface);
|
||||
mutex_unlock(&serial->disc_mutex);
|
||||
usb_serial_put(serial);
|
||||
|
||||
/* Mustn't dereference serial any more */
|
||||
if (count == 0)
|
||||
module_put(owner);
|
||||
module_put(owner);
|
||||
}
|
||||
|
||||
static void serial_close(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
|
||||
if (tty_port_close_start(&port->port, tty, filp) == 0)
|
||||
return;
|
||||
|
||||
serial_do_down(port);
|
||||
tty_port_close_end(&port->port, tty);
|
||||
tty_port_tty_set(&port->port, NULL);
|
||||
serial_do_free(port);
|
||||
}
|
||||
|
||||
static void serial_hangup(struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
serial_do_down(port);
|
||||
tty_port_hangup(&port->port);
|
||||
serial_do_free(port);
|
||||
}
|
||||
|
||||
static int serial_write(struct tty_struct *tty, const unsigned char *buf,
|
||||
@ -648,6 +675,29 @@ static struct usb_serial_driver *search_serial_device(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int serial_carrier_raised(struct tty_port *port)
|
||||
{
|
||||
struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
|
||||
struct usb_serial_driver *drv = p->serial->type;
|
||||
if (drv->carrier_raised)
|
||||
return drv->carrier_raised(p);
|
||||
/* No carrier control - don't block */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void serial_dtr_rts(struct tty_port *port, int on)
|
||||
{
|
||||
struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
|
||||
struct usb_serial_driver *drv = p->serial->type;
|
||||
if (drv->dtr_rts)
|
||||
drv->dtr_rts(p, on);
|
||||
}
|
||||
|
||||
static const struct tty_port_operations serial_port_ops = {
|
||||
.carrier_raised = serial_carrier_raised,
|
||||
.dtr_rts = serial_dtr_rts,
|
||||
};
|
||||
|
||||
int usb_serial_probe(struct usb_interface *interface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
@ -841,6 +891,7 @@ int usb_serial_probe(struct usb_interface *interface,
|
||||
if (!port)
|
||||
goto probe_error;
|
||||
tty_port_init(&port->port);
|
||||
port->port.ops = &serial_port_ops;
|
||||
port->serial = serial;
|
||||
spin_lock_init(&port->lock);
|
||||
mutex_init(&port->mutex);
|
||||
@ -1071,6 +1122,9 @@ void usb_serial_disconnect(struct usb_interface *interface)
|
||||
if (port) {
|
||||
struct tty_struct *tty = tty_port_tty_get(&port->port);
|
||||
if (tty) {
|
||||
/* The hangup will occur asynchronously but
|
||||
the object refcounts will sort out all the
|
||||
cleanup */
|
||||
tty_hangup(tty);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
@ -1135,6 +1189,7 @@ static const struct tty_operations serial_ops = {
|
||||
.open = serial_open,
|
||||
.close = serial_close,
|
||||
.write = serial_write,
|
||||
.hangup = serial_hangup,
|
||||
.write_room = serial_write_room,
|
||||
.ioctl = serial_ioctl,
|
||||
.set_termios = serial_set_termios,
|
||||
@ -1147,6 +1202,7 @@ static const struct tty_operations serial_ops = {
|
||||
.proc_fops = &serial_proc_fops,
|
||||
};
|
||||
|
||||
|
||||
struct tty_driver *usb_serial_tty_driver;
|
||||
|
||||
static int __init usb_serial_init(void)
|
||||
|
@ -38,8 +38,7 @@
|
||||
/* function prototypes for a handspring visor */
|
||||
static int visor_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void visor_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp);
|
||||
static void visor_close(struct usb_serial_port *port);
|
||||
static int visor_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
static int visor_write_room(struct tty_struct *tty);
|
||||
@ -324,8 +323,7 @@ exit:
|
||||
}
|
||||
|
||||
|
||||
static void visor_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void visor_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct visor_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned char *transfer_buffer;
|
||||
|
@ -147,8 +147,7 @@ static int whiteheat_attach(struct usb_serial *serial);
|
||||
static void whiteheat_shutdown(struct usb_serial *serial);
|
||||
static int whiteheat_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void whiteheat_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
static void whiteheat_close(struct usb_serial_port *port);
|
||||
static int whiteheat_write(struct tty_struct *tty,
|
||||
struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
@ -712,8 +711,7 @@ exit:
|
||||
}
|
||||
|
||||
|
||||
static void whiteheat_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp)
|
||||
static void whiteheat_close(struct usb_serial_port *port)
|
||||
{
|
||||
struct whiteheat_private *info = usb_get_serial_port_data(port);
|
||||
struct whiteheat_urb_wrap *wrap;
|
||||
@ -723,31 +721,7 @@ static void whiteheat_close(struct tty_struct *tty,
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
mutex_lock(&port->serial->disc_mutex);
|
||||
/* filp is NULL when called from usb_serial_disconnect */
|
||||
if ((filp && (tty_hung_up_p(filp))) || port->serial->disconnected) {
|
||||
mutex_unlock(&port->serial->disc_mutex);
|
||||
return;
|
||||
}
|
||||
mutex_unlock(&port->serial->disc_mutex);
|
||||
|
||||
tty->closing = 1;
|
||||
|
||||
/*
|
||||
* Not currently in use; tty_wait_until_sent() calls
|
||||
* serial_chars_in_buffer() which deadlocks on the second semaphore
|
||||
* acquisition. This should be fixed at some point. Greg's been
|
||||
* notified.
|
||||
if ((filp->f_flags & (O_NDELAY | O_NONBLOCK)) == 0) {
|
||||
tty_wait_until_sent(tty, CLOSING_DELAY);
|
||||
}
|
||||
*/
|
||||
|
||||
tty_driver_flush_buffer(tty);
|
||||
tty_ldisc_flush(tty);
|
||||
|
||||
firm_report_tx_done(port);
|
||||
|
||||
firm_close(port);
|
||||
|
||||
/* shutdown our bulk reads and writes */
|
||||
@ -775,10 +749,7 @@ static void whiteheat_close(struct tty_struct *tty,
|
||||
}
|
||||
spin_unlock_irq(&info->lock);
|
||||
mutex_unlock(&info->deathwarrant);
|
||||
|
||||
stop_command_port(port->serial);
|
||||
|
||||
tty->closing = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -557,8 +557,10 @@ static int __init init_devpts_fs(void)
|
||||
int err = register_filesystem(&devpts_fs_type);
|
||||
if (!err) {
|
||||
devpts_mnt = kern_mount(&devpts_fs_type);
|
||||
if (IS_ERR(devpts_mnt))
|
||||
if (IS_ERR(devpts_mnt)) {
|
||||
err = PTR_ERR(devpts_mnt);
|
||||
unregister_filesystem(&devpts_fs_type);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
@ -142,19 +142,6 @@ struct CYZ_BOOT_CTRL {
|
||||
|
||||
|
||||
#ifndef DP_WINDOW_SIZE
|
||||
/* #include "cyclomz.h" */
|
||||
/****************** ****************** *******************/
|
||||
/*
|
||||
* The data types defined below are used in all ZFIRM interface
|
||||
* data structures. They accomodate differences between HW
|
||||
* architectures and compilers.
|
||||
*/
|
||||
|
||||
typedef __u64 ucdouble; /* 64 bits, unsigned */
|
||||
typedef __u32 uclong; /* 32 bits, unsigned */
|
||||
typedef __u16 ucshort; /* 16 bits, unsigned */
|
||||
typedef __u8 ucchar; /* 8 bits, unsigned */
|
||||
|
||||
/*
|
||||
* Memory Window Sizes
|
||||
*/
|
||||
@ -507,16 +494,20 @@ struct ZFW_CTRL {
|
||||
|
||||
/* Per card data structure */
|
||||
struct cyclades_card {
|
||||
void __iomem *base_addr;
|
||||
void __iomem *ctl_addr;
|
||||
int irq;
|
||||
unsigned int num_chips; /* 0 if card absent, -1 if Z/PCI, else Y */
|
||||
unsigned int first_line; /* minor number of first channel on card */
|
||||
unsigned int nports; /* Number of ports in the card */
|
||||
int bus_index; /* address shift - 0 for ISA, 1 for PCI */
|
||||
int intr_enabled; /* FW Interrupt flag - 0 disabled, 1 enabled */
|
||||
spinlock_t card_lock;
|
||||
struct cyclades_port *ports;
|
||||
void __iomem *base_addr;
|
||||
union {
|
||||
void __iomem *p9050;
|
||||
struct RUNTIME_9060 __iomem *p9060;
|
||||
} ctl_addr;
|
||||
int irq;
|
||||
unsigned int num_chips; /* 0 if card absent, -1 if Z/PCI, else Y */
|
||||
unsigned int first_line; /* minor number of first channel on card */
|
||||
unsigned int nports; /* Number of ports in the card */
|
||||
int bus_index; /* address shift - 0 for ISA, 1 for PCI */
|
||||
int intr_enabled; /* FW Interrupt flag - 0 disabled, 1 enabled */
|
||||
u32 hw_ver;
|
||||
spinlock_t card_lock;
|
||||
struct cyclades_port *ports;
|
||||
};
|
||||
|
||||
/***************************************
|
||||
|
@ -1996,10 +1996,12 @@
|
||||
#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_U 0xC118
|
||||
#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU 0xC11C
|
||||
#define PCI_DEVICE_ID_OXSEMI_16PCI954 0x9501
|
||||
#define PCI_DEVICE_ID_OXSEMI_C950 0x950B
|
||||
#define PCI_DEVICE_ID_OXSEMI_16PCI95N 0x9511
|
||||
#define PCI_DEVICE_ID_OXSEMI_16PCI954PP 0x9513
|
||||
#define PCI_DEVICE_ID_OXSEMI_16PCI952 0x9521
|
||||
#define PCI_DEVICE_ID_OXSEMI_16PCI952PP 0x9523
|
||||
#define PCI_SUBDEVICE_ID_OXSEMI_C950 0x0001
|
||||
|
||||
#define PCI_VENDOR_ID_CHELSIO 0x1425
|
||||
|
||||
|
19
include/linux/rational.h
Normal file
19
include/linux/rational.h
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* rational fractions
|
||||
*
|
||||
* Copyright (C) 2009 emlix GmbH, Oskar Schirmer <os@emlix.com>
|
||||
*
|
||||
* helper functions when coping with rational numbers,
|
||||
* e.g. when calculating optimum numerator/denominator pairs for
|
||||
* pll configuration taking into account restricted register size
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_RATIONAL_H
|
||||
#define _LINUX_RATIONAL_H
|
||||
|
||||
void rational_best_approximation(
|
||||
unsigned long given_numerator, unsigned long given_denominator,
|
||||
unsigned long max_numerator, unsigned long max_denominator,
|
||||
unsigned long *best_numerator, unsigned long *best_denominator);
|
||||
|
||||
#endif /* _LINUX_RATIONAL_H */
|
@ -96,54 +96,76 @@ struct serial_uart_config {
|
||||
|
||||
/*
|
||||
* Definitions for async_struct (and serial_struct) flags field
|
||||
*
|
||||
* Define ASYNCB_* for convenient use with {test,set,clear}_bit.
|
||||
*/
|
||||
#define ASYNC_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
|
||||
on the callout port */
|
||||
#define ASYNC_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
|
||||
#define ASYNC_SAK 0x0004 /* Secure Attention Key (Orange book) */
|
||||
#define ASYNC_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
|
||||
#define ASYNCB_HUP_NOTIFY 0 /* Notify getty on hangups and closes
|
||||
* on the callout port */
|
||||
#define ASYNCB_FOURPORT 1 /* Set OU1, OUT2 per AST Fourport settings */
|
||||
#define ASYNCB_SAK 2 /* Secure Attention Key (Orange book) */
|
||||
#define ASYNCB_SPLIT_TERMIOS 3 /* Separate termios for dialin/callout */
|
||||
#define ASYNCB_SPD_HI 4 /* Use 56000 instead of 38400 bps */
|
||||
#define ASYNCB_SPD_VHI 5 /* Use 115200 instead of 38400 bps */
|
||||
#define ASYNCB_SKIP_TEST 6 /* Skip UART test during autoconfiguration */
|
||||
#define ASYNCB_AUTO_IRQ 7 /* Do automatic IRQ during
|
||||
* autoconfiguration */
|
||||
#define ASYNCB_SESSION_LOCKOUT 8 /* Lock out cua opens based on session */
|
||||
#define ASYNCB_PGRP_LOCKOUT 9 /* Lock out cua opens based on pgrp */
|
||||
#define ASYNCB_CALLOUT_NOHUP 10 /* Don't do hangups for cua device */
|
||||
#define ASYNCB_HARDPPS_CD 11 /* Call hardpps when CD goes high */
|
||||
#define ASYNCB_SPD_SHI 12 /* Use 230400 instead of 38400 bps */
|
||||
#define ASYNCB_LOW_LATENCY 13 /* Request low latency behaviour */
|
||||
#define ASYNCB_BUGGY_UART 14 /* This is a buggy UART, skip some safety
|
||||
* checks. Note: can be dangerous! */
|
||||
#define ASYNCB_AUTOPROBE 15 /* Port was autoprobed by PCI or PNP code */
|
||||
#define ASYNCB_LAST_USER 15
|
||||
|
||||
#define ASYNC_SPD_MASK 0x1030
|
||||
#define ASYNC_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
|
||||
/* Internal flags used only by kernel */
|
||||
#define ASYNCB_INITIALIZED 31 /* Serial port was initialized */
|
||||
#define ASYNCB_NORMAL_ACTIVE 29 /* Normal device is active */
|
||||
#define ASYNCB_BOOT_AUTOCONF 28 /* Autoconfigure port on bootup */
|
||||
#define ASYNCB_CLOSING 27 /* Serial port is closing */
|
||||
#define ASYNCB_CTS_FLOW 26 /* Do CTS flow control */
|
||||
#define ASYNCB_CHECK_CD 25 /* i.e., CLOCAL */
|
||||
#define ASYNCB_SHARE_IRQ 24 /* for multifunction cards, no longer used */
|
||||
#define ASYNCB_CONS_FLOW 23 /* flow control for console */
|
||||
#define ASYNCB_BOOT_ONLYMCA 22 /* Probe only if MCA bus */
|
||||
#define ASYNCB_FIRST_KERNEL 22
|
||||
|
||||
#define ASYNC_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
|
||||
#define ASYNC_SPD_CUST 0x0030 /* Use user-specified divisor */
|
||||
#define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY)
|
||||
#define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT)
|
||||
#define ASYNC_SAK (1U << ASYNCB_SAK)
|
||||
#define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS)
|
||||
#define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI)
|
||||
#define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI)
|
||||
#define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST)
|
||||
#define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ)
|
||||
#define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT)
|
||||
#define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT)
|
||||
#define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP)
|
||||
#define ASYNC_HARDPPS_CD (1U << ASYNCB_HARDPPS_CD)
|
||||
#define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI)
|
||||
#define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY)
|
||||
#define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART)
|
||||
#define ASYNC_AUTOPROBE (1U << ASYNCB_AUTOPROBE)
|
||||
|
||||
#define ASYNC_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
|
||||
#define ASYNC_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
|
||||
#define ASYNC_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
|
||||
#define ASYNC_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
|
||||
#define ASYNC_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
|
||||
#define ASYNC_FLAGS ((1U << ASYNCB_LAST_USER) - 1)
|
||||
#define ASYNC_USR_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI| \
|
||||
ASYNC_CALLOUT_NOHUP|ASYNC_SPD_SHI|ASYNC_LOW_LATENCY)
|
||||
#define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI)
|
||||
#define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI)
|
||||
#define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI)
|
||||
|
||||
#define ASYNC_HARDPPS_CD 0x0800 /* Call hardpps when CD goes high */
|
||||
|
||||
#define ASYNC_SPD_SHI 0x1000 /* Use 230400 instead of 38400 bps */
|
||||
#define ASYNC_SPD_WARP 0x1010 /* Use 460800 instead of 38400 bps */
|
||||
|
||||
#define ASYNC_LOW_LATENCY 0x2000 /* Request low latency behaviour */
|
||||
|
||||
#define ASYNC_BUGGY_UART 0x4000 /* This is a buggy UART, skip some safety
|
||||
* checks. Note: can be dangerous! */
|
||||
|
||||
#define ASYNC_AUTOPROBE 0x8000 /* Port was autoprobed by PCI or PNP code */
|
||||
|
||||
#define ASYNC_FLAGS 0x7FFF /* Possible legal async flags */
|
||||
#define ASYNC_USR_MASK 0x3430 /* Legal flags that non-privileged
|
||||
* users can set or reset */
|
||||
|
||||
/* Internal flags used only by kernel/chr_drv/serial.c */
|
||||
#define ASYNC_INITIALIZED 0x80000000 /* Serial port was initialized */
|
||||
#define ASYNC_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
|
||||
#define ASYNC_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
|
||||
#define ASYNC_CLOSING 0x08000000 /* Serial port is closing */
|
||||
#define ASYNC_CTS_FLOW 0x04000000 /* Do CTS flow control */
|
||||
#define ASYNC_CHECK_CD 0x02000000 /* i.e., CLOCAL */
|
||||
#define ASYNC_SHARE_IRQ 0x01000000 /* for multifunction cards
|
||||
--- no longer used */
|
||||
#define ASYNC_CONS_FLOW 0x00800000 /* flow control for console */
|
||||
|
||||
#define ASYNC_BOOT_ONLYMCA 0x00400000 /* Probe only if MCA bus */
|
||||
#define ASYNC_INTERNAL_FLAGS 0xFFC00000 /* Internal flags */
|
||||
#define ASYNC_INITIALIZED (1U << ASYNCB_INITIALIZED)
|
||||
#define ASYNC_NORMAL_ACTIVE (1U << ASYNCB_NORMAL_ACTIVE)
|
||||
#define ASYNC_BOOT_AUTOCONF (1U << ASYNCB_BOOT_AUTOCONF)
|
||||
#define ASYNC_CLOSING (1U << ASYNCB_CLOSING)
|
||||
#define ASYNC_CTS_FLOW (1U << ASYNCB_CTS_FLOW)
|
||||
#define ASYNC_CHECK_CD (1U << ASYNCB_CHECK_CD)
|
||||
#define ASYNC_SHARE_IRQ (1U << ASYNCB_SHARE_IRQ)
|
||||
#define ASYNC_CONS_FLOW (1U << ASYNCB_CONS_FLOW)
|
||||
#define ASYNC_BOOT_ONLYMCA (1U << ASYNCB_BOOT_ONLYMCA)
|
||||
#define ASYNC_INTERNAL_FLAGS (~((1U << ASYNCB_FIRST_KERNEL) - 1))
|
||||
|
||||
/*
|
||||
* Multiport serial configuration structure --- external structure
|
||||
|
@ -41,7 +41,8 @@
|
||||
#define PORT_XSCALE 15
|
||||
#define PORT_RM9000 16 /* PMC-Sierra RM9xxx internal UART */
|
||||
#define PORT_OCTEON 17 /* Cavium OCTEON internal UART */
|
||||
#define PORT_MAX_8250 17 /* max port ID */
|
||||
#define PORT_AR7 18 /* Texas Instruments AR7 internal UART */
|
||||
#define PORT_MAX_8250 18 /* max port ID */
|
||||
|
||||
/*
|
||||
* ARM specific type numbers. These are not currently guaranteed
|
||||
@ -167,6 +168,9 @@
|
||||
/* MAX3100 */
|
||||
#define PORT_MAX3100 86
|
||||
|
||||
/* Timberdale UART */
|
||||
#define PORT_TIMBUART 87
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/compiler.h>
|
||||
|
@ -185,7 +185,7 @@ struct tty_port;
|
||||
struct tty_port_operations {
|
||||
/* Return 1 if the carrier is raised */
|
||||
int (*carrier_raised)(struct tty_port *port);
|
||||
void (*raise_dtr_rts)(struct tty_port *port);
|
||||
void (*dtr_rts)(struct tty_port *port, int raise);
|
||||
};
|
||||
|
||||
struct tty_port {
|
||||
@ -201,6 +201,9 @@ struct tty_port {
|
||||
unsigned char *xmit_buf; /* Optional buffer */
|
||||
int close_delay; /* Close port delay */
|
||||
int closing_wait; /* Delay for output */
|
||||
int drain_delay; /* Set to zero if no pure time
|
||||
based drain is needed else
|
||||
set to size of fifo */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -223,8 +226,11 @@ struct tty_struct {
|
||||
struct tty_driver *driver;
|
||||
const struct tty_operations *ops;
|
||||
int index;
|
||||
/* The ldisc objects are protected by tty_ldisc_lock at the moment */
|
||||
struct tty_ldisc ldisc;
|
||||
|
||||
/* Protects ldisc changes: Lock tty not pty */
|
||||
struct mutex ldisc_mutex;
|
||||
struct tty_ldisc *ldisc;
|
||||
|
||||
struct mutex termios_mutex;
|
||||
spinlock_t ctrl_lock;
|
||||
/* Termios values are protected by the termios mutex */
|
||||
@ -311,6 +317,7 @@ struct tty_struct {
|
||||
#define TTY_CLOSING 7 /* ->close() in progress */
|
||||
#define TTY_LDISC 9 /* Line discipline attached */
|
||||
#define TTY_LDISC_CHANGING 10 /* Line discipline changing */
|
||||
#define TTY_LDISC_OPEN 11 /* Line discipline is open */
|
||||
#define TTY_HW_COOK_OUT 14 /* Hardware can do output cooking */
|
||||
#define TTY_HW_COOK_IN 15 /* Hardware can do input cooking */
|
||||
#define TTY_PTY_LOCK 16 /* pty private */
|
||||
@ -403,6 +410,7 @@ extern int tty_termios_hw_change(struct ktermios *a, struct ktermios *b);
|
||||
extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *);
|
||||
extern void tty_ldisc_deref(struct tty_ldisc *);
|
||||
extern struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *);
|
||||
extern void tty_ldisc_hangup(struct tty_struct *tty);
|
||||
extern const struct file_operations tty_ldiscs_proc_fops;
|
||||
|
||||
extern void tty_wakeup(struct tty_struct *tty);
|
||||
@ -425,6 +433,9 @@ extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
|
||||
extern void tty_release_dev(struct file *filp);
|
||||
extern int tty_init_termios(struct tty_struct *tty);
|
||||
|
||||
extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty);
|
||||
extern struct tty_struct *tty_pair_get_pty(struct tty_struct *tty);
|
||||
|
||||
extern struct mutex tty_mutex;
|
||||
|
||||
extern void tty_write_unlock(struct tty_struct *tty);
|
||||
@ -438,6 +449,7 @@ extern struct tty_struct *tty_port_tty_get(struct tty_port *port);
|
||||
extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty);
|
||||
extern int tty_port_carrier_raised(struct tty_port *port);
|
||||
extern void tty_port_raise_dtr_rts(struct tty_port *port);
|
||||
extern void tty_port_lower_dtr_rts(struct tty_port *port);
|
||||
extern void tty_port_hangup(struct tty_port *port);
|
||||
extern int tty_port_block_til_ready(struct tty_port *port,
|
||||
struct tty_struct *tty, struct file *filp);
|
||||
|
@ -127,7 +127,8 @@
|
||||
* the line discipline are close to full, and it should somehow
|
||||
* signal that no more characters should be sent to the tty.
|
||||
*
|
||||
* Optional: Always invoke via tty_throttle();
|
||||
* Optional: Always invoke via tty_throttle(), called under the
|
||||
* termios lock.
|
||||
*
|
||||
* void (*unthrottle)(struct tty_struct * tty);
|
||||
*
|
||||
@ -135,7 +136,8 @@
|
||||
* that characters can now be sent to the tty without fear of
|
||||
* overrunning the input buffers of the line disciplines.
|
||||
*
|
||||
* Optional: Always invoke via tty_unthrottle();
|
||||
* Optional: Always invoke via tty_unthrottle(), called under the
|
||||
* termios lock.
|
||||
*
|
||||
* void (*stop)(struct tty_struct *tty);
|
||||
*
|
||||
|
@ -224,8 +224,7 @@ struct usb_serial_driver {
|
||||
/* Called by console with tty = NULL and by tty */
|
||||
int (*open)(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
void (*close)(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
void (*close)(struct usb_serial_port *port);
|
||||
int (*write)(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
/* Called only by the tty layer */
|
||||
@ -241,6 +240,10 @@ struct usb_serial_driver {
|
||||
int (*tiocmget)(struct tty_struct *tty, struct file *file);
|
||||
int (*tiocmset)(struct tty_struct *tty, struct file *file,
|
||||
unsigned int set, unsigned int clear);
|
||||
/* Called by the tty layer for port level work. There may or may not
|
||||
be an attached tty at this point */
|
||||
void (*dtr_rts)(struct usb_serial_port *port, int on);
|
||||
int (*carrier_raised)(struct usb_serial_port *port);
|
||||
/* USB events */
|
||||
void (*read_int_callback)(struct urb *urb);
|
||||
void (*write_int_callback)(struct urb *urb);
|
||||
@ -283,8 +286,7 @@ extern int usb_serial_generic_open(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
extern int usb_serial_generic_write(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, const unsigned char *buf, int count);
|
||||
extern void usb_serial_generic_close(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct file *filp);
|
||||
extern void usb_serial_generic_close(struct usb_serial_port *port);
|
||||
extern int usb_serial_generic_resume(struct usb_serial *serial);
|
||||
extern int usb_serial_generic_write_room(struct tty_struct *tty);
|
||||
extern int usb_serial_generic_chars_in_buffer(struct tty_struct *tty);
|
||||
|
@ -10,6 +10,9 @@ menu "Library routines"
|
||||
config BITREVERSE
|
||||
tristate
|
||||
|
||||
config RATIONAL
|
||||
boolean
|
||||
|
||||
config GENERIC_FIND_FIRST_BIT
|
||||
bool
|
||||
|
||||
|
@ -50,6 +50,7 @@ ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_BITREVERSE) += bitrev.o
|
||||
obj-$(CONFIG_RATIONAL) += rational.o
|
||||
obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o
|
||||
obj-$(CONFIG_CRC16) += crc16.o
|
||||
obj-$(CONFIG_CRC_T10DIF)+= crc-t10dif.o
|
||||
|
62
lib/rational.c
Normal file
62
lib/rational.c
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* rational fractions
|
||||
*
|
||||
* Copyright (C) 2009 emlix GmbH, Oskar Schirmer <os@emlix.com>
|
||||
*
|
||||
* helper functions when coping with rational numbers
|
||||
*/
|
||||
|
||||
#include <linux/rational.h>
|
||||
|
||||
/*
|
||||
* calculate best rational approximation for a given fraction
|
||||
* taking into account restricted register size, e.g. to find
|
||||
* appropriate values for a pll with 5 bit denominator and
|
||||
* 8 bit numerator register fields, trying to set up with a
|
||||
* frequency ratio of 3.1415, one would say:
|
||||
*
|
||||
* rational_best_approximation(31415, 10000,
|
||||
* (1 << 8) - 1, (1 << 5) - 1, &n, &d);
|
||||
*
|
||||
* you may look at given_numerator as a fixed point number,
|
||||
* with the fractional part size described in given_denominator.
|
||||
*
|
||||
* for theoretical background, see:
|
||||
* http://en.wikipedia.org/wiki/Continued_fraction
|
||||
*/
|
||||
|
||||
void rational_best_approximation(
|
||||
unsigned long given_numerator, unsigned long given_denominator,
|
||||
unsigned long max_numerator, unsigned long max_denominator,
|
||||
unsigned long *best_numerator, unsigned long *best_denominator)
|
||||
{
|
||||
unsigned long n, d, n0, d0, n1, d1;
|
||||
n = given_numerator;
|
||||
d = given_denominator;
|
||||
n0 = d1 = 0;
|
||||
n1 = d0 = 1;
|
||||
for (;;) {
|
||||
unsigned long t, a;
|
||||
if ((n1 > max_numerator) || (d1 > max_denominator)) {
|
||||
n1 = n0;
|
||||
d1 = d0;
|
||||
break;
|
||||
}
|
||||
if (d == 0)
|
||||
break;
|
||||
t = d;
|
||||
a = n / d;
|
||||
d = n % d;
|
||||
n = t;
|
||||
t = n0 + a * n1;
|
||||
n0 = n1;
|
||||
n1 = t;
|
||||
t = d0 + a * d1;
|
||||
d0 = d1;
|
||||
d1 = t;
|
||||
}
|
||||
*best_numerator = n1;
|
||||
*best_denominator = d1;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(rational_best_approximation);
|
Loading…
Reference in New Issue
Block a user