tty: n_smux: Add SMUX TTY Line Discipline Driver
Add new Serial Multiplexer (SMUX) driver. This driver multiplexes multiple logical channels over a single physical HSUART channel using the TTY Line Discipline framework. This driver will be used in Fusion 4 for control plane, data plane and DIAG traffic between Application processor and QSC modem. Change-Id: Ibecf6cea872f5baf11fb93ded6124243a37a2085 Signed-off-by: Eric Holmberg <eholmber@codeaurora.org> Signed-off-by: Angshuman Sarkar <angshuman@codeaurora.org> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org> Signed-off-by: David Brown <davidb@codeaurora.org>
This commit is contained in:
committed by
Stephen Boyd
parent
6944ad25e1
commit
d68384d72e
@@ -323,6 +323,19 @@ config N_GSM
|
|||||||
This line discipline provides support for the GSM MUX protocol and
|
This line discipline provides support for the GSM MUX protocol and
|
||||||
presents the mux as a set of 61 individual tty devices.
|
presents the mux as a set of 61 individual tty devices.
|
||||||
|
|
||||||
|
config N_SMUX
|
||||||
|
tristate "SMUX line discipline support"
|
||||||
|
depends on NET && SERIAL_MSM_HS
|
||||||
|
help
|
||||||
|
This line discipline provides support for the Serial MUX protocol
|
||||||
|
and provides a TTY and kernel API for multiple logical channels.
|
||||||
|
|
||||||
|
config N_SMUX_LOOPBACK
|
||||||
|
tristate "SMUX line discipline loopback support"
|
||||||
|
depends on N_SMUX
|
||||||
|
help
|
||||||
|
Provides loopback and unit testing support for the Serial MUX Protocol.
|
||||||
|
|
||||||
config TRACE_ROUTER
|
config TRACE_ROUTER
|
||||||
tristate "Trace data router for MIPI P1149.7 cJTAG standard"
|
tristate "Trace data router for MIPI P1149.7 cJTAG standard"
|
||||||
depends on TRACE_SINK
|
depends on TRACE_SINK
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ obj-$(CONFIG_AUDIT) += tty_audit.o
|
|||||||
obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o
|
obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o
|
||||||
obj-$(CONFIG_N_HDLC) += n_hdlc.o
|
obj-$(CONFIG_N_HDLC) += n_hdlc.o
|
||||||
obj-$(CONFIG_N_GSM) += n_gsm.o
|
obj-$(CONFIG_N_GSM) += n_gsm.o
|
||||||
|
obj-$(CONFIG_N_SMUX) += n_smux.o
|
||||||
|
obj-$(CONFIG_N_SMUX_LOOPBACK) += smux_test.o smux_loopback.o
|
||||||
obj-$(CONFIG_TRACE_ROUTER) += n_tracerouter.o
|
obj-$(CONFIG_TRACE_ROUTER) += n_tracerouter.o
|
||||||
obj-$(CONFIG_TRACE_SINK) += n_tracesink.o
|
obj-$(CONFIG_TRACE_SINK) += n_tracesink.o
|
||||||
obj-$(CONFIG_R3964) += n_r3964.o
|
obj-$(CONFIG_R3964) += n_r3964.o
|
||||||
|
|||||||
2938
drivers/tty/n_smux.c
Normal file
2938
drivers/tty/n_smux.c
Normal file
File diff suppressed because it is too large
Load Diff
289
drivers/tty/smux_loopback.c
Normal file
289
drivers/tty/smux_loopback.c
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
/* drivers/tty/smux_loopback.c
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/kfifo.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/smux.h>
|
||||||
|
#include "smux_private.h"
|
||||||
|
|
||||||
|
#define SMUX_LOOP_FIFO_SIZE 128
|
||||||
|
|
||||||
|
static void smux_loopback_rx_worker(struct work_struct *work);
|
||||||
|
static struct workqueue_struct *smux_loopback_wq;
|
||||||
|
static DECLARE_WORK(smux_loopback_work, smux_loopback_rx_worker);
|
||||||
|
static struct kfifo smux_loop_pkt_fifo;
|
||||||
|
static DEFINE_SPINLOCK(hw_fn_lock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize loopback framework (called by n_smux.c).
|
||||||
|
*/
|
||||||
|
int smux_loopback_init(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
spin_lock_init(&hw_fn_lock);
|
||||||
|
smux_loopback_wq = create_singlethread_workqueue("smux_loopback_wq");
|
||||||
|
if (IS_ERR(smux_loopback_wq)) {
|
||||||
|
pr_err("%s: failed to create workqueue\n", __func__);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret |= kfifo_alloc(&smux_loop_pkt_fifo,
|
||||||
|
SMUX_LOOP_FIFO_SIZE * sizeof(struct smux_pkt_t *),
|
||||||
|
GFP_KERNEL);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate a write to the TTY hardware by duplicating
|
||||||
|
* the TX packet and putting it into the RX queue.
|
||||||
|
*
|
||||||
|
* @pkt Packet to write
|
||||||
|
*
|
||||||
|
* @returns 0 on success
|
||||||
|
*/
|
||||||
|
int smux_tx_loopback(struct smux_pkt_t *pkt_ptr)
|
||||||
|
{
|
||||||
|
struct smux_pkt_t *send_pkt;
|
||||||
|
unsigned long flags;
|
||||||
|
int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* duplicate packet */
|
||||||
|
send_pkt = smux_alloc_pkt();
|
||||||
|
send_pkt->hdr = pkt_ptr->hdr;
|
||||||
|
if (pkt_ptr->hdr.payload_len) {
|
||||||
|
ret = smux_alloc_pkt_payload(send_pkt);
|
||||||
|
if (ret) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
memcpy(send_pkt->payload, pkt_ptr->payload,
|
||||||
|
pkt_ptr->hdr.payload_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* queue duplicate as pseudo-RX data */
|
||||||
|
spin_lock_irqsave(&hw_fn_lock, flags);
|
||||||
|
i = kfifo_avail(&smux_loop_pkt_fifo);
|
||||||
|
if (i < sizeof(struct smux_pkt_t *)) {
|
||||||
|
pr_err("%s: no space in fifo\n", __func__);
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = kfifo_in(&smux_loop_pkt_fifo,
|
||||||
|
&send_pkt,
|
||||||
|
sizeof(struct smux_pkt_t *));
|
||||||
|
if (i < 0) {
|
||||||
|
pr_err("%s: fifo error\n", __func__);
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
queue_work(smux_loopback_wq, &smux_loopback_work);
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
spin_unlock_irqrestore(&hw_fn_lock, flags);
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive loopback byte processor.
|
||||||
|
*
|
||||||
|
* @pkt Incoming packet
|
||||||
|
*/
|
||||||
|
static void smux_loopback_rx_byte(struct smux_pkt_t *pkt)
|
||||||
|
{
|
||||||
|
static int simulated_retry_cnt;
|
||||||
|
const char ack = SMUX_WAKEUP_ACK;
|
||||||
|
|
||||||
|
switch (pkt->hdr.flags) {
|
||||||
|
case SMUX_WAKEUP_REQ:
|
||||||
|
/* reply with ACK after appropriate delays */
|
||||||
|
++simulated_retry_cnt;
|
||||||
|
if (simulated_retry_cnt >= smux_simulate_wakeup_delay) {
|
||||||
|
pr_err("%s: completed %d of %d\n",
|
||||||
|
__func__, simulated_retry_cnt,
|
||||||
|
smux_simulate_wakeup_delay);
|
||||||
|
pr_err("%s: simulated wakeup\n", __func__);
|
||||||
|
simulated_retry_cnt = 0;
|
||||||
|
smux_rx_state_machine(&ack, 1, 0);
|
||||||
|
} else {
|
||||||
|
/* force retry */
|
||||||
|
pr_err("%s: dropping wakeup request %d of %d\n",
|
||||||
|
__func__, simulated_retry_cnt,
|
||||||
|
smux_simulate_wakeup_delay);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SMUX_WAKEUP_ACK:
|
||||||
|
/* this shouldn't happen since we don't send requests */
|
||||||
|
pr_err("%s: wakeup ACK unexpected\n", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* invalid character */
|
||||||
|
pr_err("%s: invalid character 0x%x\n",
|
||||||
|
__func__, (unsigned)pkt->hdr.flags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulated remote hardware used for local loopback testing.
|
||||||
|
*
|
||||||
|
* @work Not used
|
||||||
|
*/
|
||||||
|
static void smux_loopback_rx_worker(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct smux_pkt_t *pkt;
|
||||||
|
struct smux_pkt_t reply_pkt;
|
||||||
|
char *data;
|
||||||
|
int len;
|
||||||
|
int lcid;
|
||||||
|
int i;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
data = kzalloc(SMUX_MAX_PKT_SIZE, GFP_ATOMIC);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hw_fn_lock, flags);
|
||||||
|
while (kfifo_len(&smux_loop_pkt_fifo) >= sizeof(struct smux_pkt_t *)) {
|
||||||
|
i = kfifo_out(&smux_loop_pkt_fifo, &pkt,
|
||||||
|
sizeof(struct smux_pkt_t *));
|
||||||
|
spin_unlock_irqrestore(&hw_fn_lock, flags);
|
||||||
|
|
||||||
|
if (pkt->hdr.magic != SMUX_MAGIC) {
|
||||||
|
pr_err("%s: invalid magic %x\n", __func__,
|
||||||
|
pkt->hdr.magic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lcid = pkt->hdr.lcid;
|
||||||
|
if (smux_assert_lch_id(lcid)) {
|
||||||
|
pr_err("%s: invalid channel id %d\n", __func__, lcid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pkt->hdr.cmd) {
|
||||||
|
case SMUX_CMD_OPEN_LCH:
|
||||||
|
if (pkt->hdr.flags & SMUX_CMD_OPEN_ACK)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Reply with Open ACK */
|
||||||
|
smux_init_pkt(&reply_pkt);
|
||||||
|
reply_pkt.hdr.lcid = lcid;
|
||||||
|
reply_pkt.hdr.cmd = SMUX_CMD_OPEN_LCH;
|
||||||
|
reply_pkt.hdr.flags = SMUX_CMD_OPEN_ACK
|
||||||
|
| SMUX_CMD_OPEN_POWER_COLLAPSE;
|
||||||
|
reply_pkt.hdr.payload_len = 0;
|
||||||
|
reply_pkt.hdr.pad_len = 0;
|
||||||
|
smux_serialize(&reply_pkt, data, &len);
|
||||||
|
smux_rx_state_machine(data, len, 0);
|
||||||
|
|
||||||
|
/* Send Remote Open */
|
||||||
|
smux_init_pkt(&reply_pkt);
|
||||||
|
reply_pkt.hdr.lcid = lcid;
|
||||||
|
reply_pkt.hdr.cmd = SMUX_CMD_OPEN_LCH;
|
||||||
|
reply_pkt.hdr.flags = SMUX_CMD_OPEN_POWER_COLLAPSE;
|
||||||
|
reply_pkt.hdr.payload_len = 0;
|
||||||
|
reply_pkt.hdr.pad_len = 0;
|
||||||
|
smux_serialize(&reply_pkt, data, &len);
|
||||||
|
smux_rx_state_machine(data, len, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMUX_CMD_CLOSE_LCH:
|
||||||
|
if (pkt->hdr.flags == SMUX_CMD_CLOSE_ACK)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Reply with Close ACK */
|
||||||
|
smux_init_pkt(&reply_pkt);
|
||||||
|
reply_pkt.hdr.lcid = lcid;
|
||||||
|
reply_pkt.hdr.cmd = SMUX_CMD_CLOSE_LCH;
|
||||||
|
reply_pkt.hdr.flags = SMUX_CMD_CLOSE_ACK;
|
||||||
|
reply_pkt.hdr.payload_len = 0;
|
||||||
|
reply_pkt.hdr.pad_len = 0;
|
||||||
|
smux_serialize(&reply_pkt, data, &len);
|
||||||
|
smux_rx_state_machine(data, len, 0);
|
||||||
|
|
||||||
|
/* Send Remote Close */
|
||||||
|
smux_init_pkt(&reply_pkt);
|
||||||
|
reply_pkt.hdr.lcid = lcid;
|
||||||
|
reply_pkt.hdr.cmd = SMUX_CMD_CLOSE_LCH;
|
||||||
|
reply_pkt.hdr.flags = 0;
|
||||||
|
reply_pkt.hdr.payload_len = 0;
|
||||||
|
reply_pkt.hdr.pad_len = 0;
|
||||||
|
smux_serialize(&reply_pkt, data, &len);
|
||||||
|
smux_rx_state_machine(data, len, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMUX_CMD_DATA:
|
||||||
|
/* Echo back received data */
|
||||||
|
smux_init_pkt(&reply_pkt);
|
||||||
|
reply_pkt.hdr.lcid = lcid;
|
||||||
|
reply_pkt.hdr.cmd = SMUX_CMD_DATA;
|
||||||
|
reply_pkt.hdr.flags = 0;
|
||||||
|
reply_pkt.hdr.payload_len = pkt->hdr.payload_len;
|
||||||
|
reply_pkt.payload = pkt->payload;
|
||||||
|
reply_pkt.hdr.pad_len = pkt->hdr.pad_len;
|
||||||
|
smux_serialize(&reply_pkt, data, &len);
|
||||||
|
smux_rx_state_machine(data, len, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMUX_CMD_STATUS:
|
||||||
|
/* Echo back received status */
|
||||||
|
smux_init_pkt(&reply_pkt);
|
||||||
|
reply_pkt.hdr.lcid = lcid;
|
||||||
|
reply_pkt.hdr.cmd = SMUX_CMD_STATUS;
|
||||||
|
reply_pkt.hdr.flags = pkt->hdr.flags;
|
||||||
|
reply_pkt.hdr.payload_len = 0;
|
||||||
|
reply_pkt.payload = NULL;
|
||||||
|
reply_pkt.hdr.pad_len = pkt->hdr.pad_len;
|
||||||
|
smux_serialize(&reply_pkt, data, &len);
|
||||||
|
smux_rx_state_machine(data, len, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMUX_CMD_PWR_CTL:
|
||||||
|
/* reply with ack */
|
||||||
|
smux_init_pkt(&reply_pkt);
|
||||||
|
reply_pkt.hdr.lcid = lcid;
|
||||||
|
reply_pkt.hdr.cmd = SMUX_CMD_PWR_CTL;
|
||||||
|
reply_pkt.hdr.flags = SMUX_CMD_PWR_CTL_SLEEP_REQ
|
||||||
|
| SMUX_CMD_PWR_CTL_ACK;
|
||||||
|
reply_pkt.hdr.payload_len = 0;
|
||||||
|
reply_pkt.payload = NULL;
|
||||||
|
reply_pkt.hdr.pad_len = pkt->hdr.pad_len;
|
||||||
|
smux_serialize(&reply_pkt, data, &len);
|
||||||
|
smux_rx_state_machine(data, len, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMUX_CMD_BYTE:
|
||||||
|
smux_loopback_rx_byte(pkt);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pr_err("%s: unknown command %d\n",
|
||||||
|
__func__, pkt->hdr.cmd);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
smux_free_pkt(pkt);
|
||||||
|
spin_lock_irqsave(&hw_fn_lock, flags);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&hw_fn_lock, flags);
|
||||||
|
kfree(data);
|
||||||
|
}
|
||||||
39
drivers/tty/smux_loopback.h
Normal file
39
drivers/tty/smux_loopback.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/* drivers/tty/smux_loopback.h
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef SMUX_LOOPBACK_H
|
||||||
|
#define SMUX_LOOPBACK_H
|
||||||
|
|
||||||
|
#include "smux_private.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_N_SMUX_LOOPBACK
|
||||||
|
|
||||||
|
int smux_loopback_init(void);
|
||||||
|
int smux_tx_loopback(struct smux_pkt_t *pkt_ptr);
|
||||||
|
|
||||||
|
#else
|
||||||
|
static inline int smux_loopback_init(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int smux_tx_loopback(struct smux_pkt_t *pkt_ptr)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* CONFIG_N_SMUX_LOOPBACK */
|
||||||
|
#endif /* SMUX_LOOPBACK_H */
|
||||||
|
|
||||||
115
drivers/tty/smux_private.h
Normal file
115
drivers/tty/smux_private.h
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/* drivers/tty/smux_private.h
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef SMUX_PRIVATE_H
|
||||||
|
#define SMUX_PRIVATE_H
|
||||||
|
|
||||||
|
#define SMUX_MAX_PKT_SIZE 8192
|
||||||
|
|
||||||
|
/* SMUX Protocol Characters */
|
||||||
|
#define SMUX_MAGIC 0x33FC
|
||||||
|
#define SMUX_MAGIC_WORD1 0xFC
|
||||||
|
#define SMUX_MAGIC_WORD2 0x33
|
||||||
|
#define SMUX_WAKEUP_REQ 0xFD
|
||||||
|
#define SMUX_WAKEUP_ACK 0xFE
|
||||||
|
|
||||||
|
/* Unit testing characters */
|
||||||
|
#define SMUX_UT_ECHO_REQ 0xF0
|
||||||
|
#define SMUX_UT_ECHO_ACK_OK 0xF1
|
||||||
|
#define SMUX_UT_ECHO_ACK_FAIL 0xF2
|
||||||
|
|
||||||
|
struct tty_struct;
|
||||||
|
|
||||||
|
/* Packet header. */
|
||||||
|
struct smux_hdr_t {
|
||||||
|
uint16_t magic;
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t pad_len;
|
||||||
|
uint8_t lcid;
|
||||||
|
uint16_t payload_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Internal packet structure. */
|
||||||
|
struct smux_pkt_t {
|
||||||
|
struct smux_hdr_t hdr;
|
||||||
|
int allocated;
|
||||||
|
unsigned char *payload;
|
||||||
|
int free_payload;
|
||||||
|
struct list_head list;
|
||||||
|
void *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* SMUX Packet Commands */
|
||||||
|
enum {
|
||||||
|
SMUX_CMD_DATA = 0x0,
|
||||||
|
SMUX_CMD_OPEN_LCH = 0x1,
|
||||||
|
SMUX_CMD_CLOSE_LCH = 0x2,
|
||||||
|
SMUX_CMD_STATUS = 0x3,
|
||||||
|
SMUX_CMD_PWR_CTL = 0x4,
|
||||||
|
|
||||||
|
SMUX_CMD_BYTE, /* for internal usage */
|
||||||
|
SMUX_NUM_COMMANDS
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Open command flags */
|
||||||
|
enum {
|
||||||
|
SMUX_CMD_OPEN_ACK = 1 << 0,
|
||||||
|
SMUX_CMD_OPEN_POWER_COLLAPSE = 1 << 1,
|
||||||
|
SMUX_CMD_OPEN_REMOTE_LOOPBACK = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Close command flags */
|
||||||
|
enum {
|
||||||
|
SMUX_CMD_CLOSE_ACK = 1 << 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Power command flags */
|
||||||
|
enum {
|
||||||
|
SMUX_CMD_PWR_CTL_ACK = 1 << 0,
|
||||||
|
SMUX_CMD_PWR_CTL_SLEEP_REQ = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Local logical channel states */
|
||||||
|
enum {
|
||||||
|
SMUX_LCH_LOCAL_CLOSED,
|
||||||
|
SMUX_LCH_LOCAL_OPENING,
|
||||||
|
SMUX_LCH_LOCAL_OPENED,
|
||||||
|
SMUX_LCH_LOCAL_CLOSING,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Remote logical channel states */
|
||||||
|
enum {
|
||||||
|
SMUX_LCH_REMOTE_CLOSED,
|
||||||
|
SMUX_LCH_REMOTE_OPENED,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int smux_assert_lch_id(uint32_t lcid);
|
||||||
|
void smux_init_pkt(struct smux_pkt_t *pkt);
|
||||||
|
struct smux_pkt_t *smux_alloc_pkt(void);
|
||||||
|
int smux_alloc_pkt_payload(struct smux_pkt_t *pkt);
|
||||||
|
void smux_free_pkt(struct smux_pkt_t *pkt);
|
||||||
|
int smux_serialize(struct smux_pkt_t *pkt, char *out,
|
||||||
|
unsigned int *out_len);
|
||||||
|
|
||||||
|
void smux_rx_state_machine(const unsigned char *data, int len, int flag);
|
||||||
|
void smuxld_receive_buf(struct tty_struct *tty, const unsigned char *cp,
|
||||||
|
char *fp, int count);
|
||||||
|
|
||||||
|
/* testing parameters */
|
||||||
|
extern int smux_byte_loopback;
|
||||||
|
extern int smux_simulate_wakeup_delay;
|
||||||
|
|
||||||
|
#endif /* SMUX_PRIVATE_H */
|
||||||
1222
drivers/tty/smux_test.c
Normal file
1222
drivers/tty/smux_test.c
Normal file
File diff suppressed because it is too large
Load Diff
295
include/linux/smux.h
Normal file
295
include/linux/smux.h
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
/* include/linux/smux.h
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef SMUX_H
|
||||||
|
#define SMUX_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logical Channel IDs
|
||||||
|
*
|
||||||
|
* This must be identical between local and remote clients.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
/* Data Ports */
|
||||||
|
SMUX_DATA_0,
|
||||||
|
SMUX_DATA_1,
|
||||||
|
SMUX_DATA_2,
|
||||||
|
SMUX_DATA_3,
|
||||||
|
SMUX_DATA_4,
|
||||||
|
SMUX_DATA_5,
|
||||||
|
SMUX_DATA_6,
|
||||||
|
SMUX_DATA_7,
|
||||||
|
SMUX_DATA_8,
|
||||||
|
SMUX_DATA_9,
|
||||||
|
SMUX_USB_RMNET_DATA_0,
|
||||||
|
SMUX_USB_DUN_0,
|
||||||
|
SMUX_USB_DIAG_0,
|
||||||
|
SMUX_SYS_MONITOR_0,
|
||||||
|
SMUX_CSVT_0,
|
||||||
|
/* add new data ports here */
|
||||||
|
|
||||||
|
/* Control Ports */
|
||||||
|
SMUX_DATA_CTL_0 = 32,
|
||||||
|
SMUX_DATA_CTL_1,
|
||||||
|
SMUX_DATA_CTL_2,
|
||||||
|
SMUX_DATA_CTL_3,
|
||||||
|
SMUX_DATA_CTL_4,
|
||||||
|
SMUX_DATA_CTL_5,
|
||||||
|
SMUX_DATA_CTL_6,
|
||||||
|
SMUX_DATA_CTL_7,
|
||||||
|
SMUX_DATA_CTL_8,
|
||||||
|
SMUX_DATA_CTL_9,
|
||||||
|
SMUX_USB_RMNET_CTL_0,
|
||||||
|
SMUX_USB_DUN_CTL_0_UNUSED,
|
||||||
|
SMUX_USB_DIAG_CTL_0,
|
||||||
|
SMUX_SYS_MONITOR_CTL_0,
|
||||||
|
SMUX_CSVT_CTL_0,
|
||||||
|
/* add new control ports here */
|
||||||
|
|
||||||
|
SMUX_TEST_LCID,
|
||||||
|
SMUX_NUM_LOGICAL_CHANNELS,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification events that are passed to the notify() function.
|
||||||
|
*
|
||||||
|
* If the @metadata argument in the notifier is non-null, then it will
|
||||||
|
* point to the associated struct smux_meta_* structure.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
SMUX_CONNECTED, /* @metadata is null */
|
||||||
|
SMUX_DISCONNECTED,
|
||||||
|
SMUX_READ_DONE,
|
||||||
|
SMUX_READ_FAIL,
|
||||||
|
SMUX_WRITE_DONE,
|
||||||
|
SMUX_WRITE_FAIL,
|
||||||
|
SMUX_TIOCM_UPDATE,
|
||||||
|
SMUX_LOW_WM_HIT, /* @metadata is NULL */
|
||||||
|
SMUX_HIGH_WM_HIT, /* @metadata is NULL */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Channel options used to modify channel behavior.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
SMUX_CH_OPTION_LOCAL_LOOPBACK = 1 << 0,
|
||||||
|
SMUX_CH_OPTION_REMOTE_LOOPBACK = 1 << 1,
|
||||||
|
SMUX_CH_OPTION_REMOTE_TX_STOP = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata for SMUX_DISCONNECTED notification
|
||||||
|
*
|
||||||
|
* @is_ssr: Disconnect caused by subsystem restart
|
||||||
|
*/
|
||||||
|
struct smux_meta_disconnected {
|
||||||
|
int is_ssr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata for SMUX_READ_DONE/SMUX_READ_FAIL notification
|
||||||
|
*
|
||||||
|
* @pkt_priv: Packet-specific private data
|
||||||
|
* @buffer: Buffer pointer passed into msm_smux_write
|
||||||
|
* @len: Buffer length passed into msm_smux_write
|
||||||
|
*/
|
||||||
|
struct smux_meta_read {
|
||||||
|
void *pkt_priv;
|
||||||
|
void *buffer;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata for SMUX_WRITE_DONE/SMUX_WRITE_FAIL notification
|
||||||
|
*
|
||||||
|
* @pkt_priv: Packet-specific private data
|
||||||
|
* @buffer: Buffer pointer returned by get_rx_buffer()
|
||||||
|
* @len: Buffer length returned by get_rx_buffer()
|
||||||
|
*/
|
||||||
|
struct smux_meta_write {
|
||||||
|
void *pkt_priv;
|
||||||
|
void *buffer;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata for SMUX_TIOCM_UPDATE notification
|
||||||
|
*
|
||||||
|
* @tiocm_old: Previous TIOCM state
|
||||||
|
* @tiocm_new: Current TIOCM state
|
||||||
|
*/
|
||||||
|
struct smux_meta_tiocm {
|
||||||
|
uint32_t tiocm_old;
|
||||||
|
uint32_t tiocm_new;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_N_SMUX
|
||||||
|
/**
|
||||||
|
* Starts the opening sequence for a logical channel.
|
||||||
|
*
|
||||||
|
* @lcid Logical channel ID
|
||||||
|
* @priv Free for client usage
|
||||||
|
* @notify Event notification function
|
||||||
|
* @get_rx_buffer Function used to provide a receive buffer to SMUX
|
||||||
|
*
|
||||||
|
* @returns 0 for success, <0 otherwise
|
||||||
|
*
|
||||||
|
* A channel must be fully closed (either not previously opened or
|
||||||
|
* msm_smux_close() has been called and the SMUX_DISCONNECTED has been
|
||||||
|
* recevied.
|
||||||
|
*
|
||||||
|
* One the remote side is opened, the client will receive a SMUX_CONNECTED
|
||||||
|
* event.
|
||||||
|
*/
|
||||||
|
int msm_smux_open(uint8_t lcid, void *priv,
|
||||||
|
void (*notify)(void *priv, int event_type, const void *metadata),
|
||||||
|
int (*get_rx_buffer)(void *priv, void **pkt_priv,
|
||||||
|
void **buffer, int size));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the closing sequence for a logical channel.
|
||||||
|
*
|
||||||
|
* @lcid Logical channel ID
|
||||||
|
* @returns 0 for success, <0 otherwise
|
||||||
|
*
|
||||||
|
* Once the close event has been acknowledge by the remote side, the client
|
||||||
|
* will receive a SMUX_DISCONNECTED notification.
|
||||||
|
*/
|
||||||
|
int msm_smux_close(uint8_t lcid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to a logical channel.
|
||||||
|
*
|
||||||
|
* @lcid Logical channel ID
|
||||||
|
* @pkt_priv Client data that will be returned with the SMUX_WRITE_DONE or
|
||||||
|
* SMUX_WRITE_FAIL notification.
|
||||||
|
* @data Data to write
|
||||||
|
* @len Length of @data
|
||||||
|
*
|
||||||
|
* @returns 0 for success, <0 otherwise
|
||||||
|
*
|
||||||
|
* Data may be written immediately after msm_smux_open() is called, but
|
||||||
|
* the data will wait in the transmit queue until the channel has been
|
||||||
|
* fully opened.
|
||||||
|
*
|
||||||
|
* Once the data has been written, the client will receive either a completion
|
||||||
|
* (SMUX_WRITE_DONE) or a failure notice (SMUX_WRITE_FAIL).
|
||||||
|
*/
|
||||||
|
int msm_smux_write(uint8_t lcid, void *pkt_priv, const void *data, int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the TX queue is currently full (high water mark).
|
||||||
|
*
|
||||||
|
* @lcid Logical channel ID
|
||||||
|
*
|
||||||
|
* @returns 0 if channel is not full; 1 if it is full; < 0 for error
|
||||||
|
*/
|
||||||
|
int msm_smux_is_ch_full(uint8_t lcid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the TX queue has space for more packets it is at or
|
||||||
|
* below the low water mark).
|
||||||
|
*
|
||||||
|
* @lcid Logical channel ID
|
||||||
|
*
|
||||||
|
* @returns 0 if channel is above low watermark
|
||||||
|
* 1 if it's at or below the low watermark
|
||||||
|
* < 0 for error
|
||||||
|
*/
|
||||||
|
int msm_smux_is_ch_low(uint8_t lcid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the TIOCM status bits.
|
||||||
|
*
|
||||||
|
* @lcid Logical channel ID
|
||||||
|
*
|
||||||
|
* @returns >= 0 TIOCM status bits
|
||||||
|
* < 0 Error condition
|
||||||
|
*/
|
||||||
|
long msm_smux_tiocm_get(uint8_t lcid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set/clear the TIOCM status bits.
|
||||||
|
*
|
||||||
|
* @lcid Logical channel ID
|
||||||
|
* @set Bits to set
|
||||||
|
* @clear Bits to clear
|
||||||
|
*
|
||||||
|
* @returns 0 for success; < 0 for failure
|
||||||
|
*
|
||||||
|
* If a bit is specified in both the @set and @clear masks, then the clear bit
|
||||||
|
* definition will dominate and the bit will be cleared.
|
||||||
|
*/
|
||||||
|
int msm_smux_tiocm_set(uint8_t lcid, uint32_t set, uint32_t clear);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set or clear channel option using the SMUX_CH_OPTION_* channel
|
||||||
|
* flags.
|
||||||
|
*
|
||||||
|
* @lcid Logical channel ID
|
||||||
|
* @set Options to set
|
||||||
|
* @clear Options to clear
|
||||||
|
*
|
||||||
|
* @returns 0 for success, < 0 for failure
|
||||||
|
*/
|
||||||
|
int msm_smux_set_ch_option(uint8_t lcid, uint32_t set, uint32_t clear);
|
||||||
|
|
||||||
|
#else
|
||||||
|
int msm_smux_open(uint8_t lcid, void *priv,
|
||||||
|
void (*notify)(void *priv, int event_type, const void *metadata),
|
||||||
|
int (*get_rx_buffer)(void *priv, void **pkt_priv,
|
||||||
|
void **buffer, int size))
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
int msm_smux_close(uint8_t lcid)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
int msm_smux_write(uint8_t lcid, void *pkt_priv, const void *data, int len)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
int msm_smux_is_ch_full(uint8_t lcid);
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
int msm_smux_is_ch_low(uint8_t lcid);
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
long msm_smux_tiocm_get(uint8_t lcid)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int msm_smux_tiocm_set(uint8_t lcid, uint32_t set, uint32_t clear)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
int msm_smux_set_ch_option(uint8_t lcid, uint32_t set, uint32_t clear)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_N_SMUX */
|
||||||
|
|
||||||
|
#endif /* SMUX_H */
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
#define N_TI_WL 22 /* for TI's WL BT, FM, GPS combo chips */
|
#define N_TI_WL 22 /* for TI's WL BT, FM, GPS combo chips */
|
||||||
#define N_TRACESINK 23 /* Trace data routing for MIPI P1149.7 */
|
#define N_TRACESINK 23 /* Trace data routing for MIPI P1149.7 */
|
||||||
#define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */
|
#define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */
|
||||||
|
#define N_SMUX 25 /* Serial MUX */
|
||||||
|
|
||||||
#ifdef __KERNEL__
|
#ifdef __KERNEL__
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
|||||||
Reference in New Issue
Block a user