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
|
||||
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
|
||||
tristate "Trace data router for MIPI P1149.7 cJTAG standard"
|
||||
depends on TRACE_SINK
|
||||
|
||||
@@ -6,6 +6,8 @@ obj-$(CONFIG_AUDIT) += tty_audit.o
|
||||
obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o
|
||||
obj-$(CONFIG_N_HDLC) += n_hdlc.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_SINK) += n_tracesink.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_TRACESINK 23 /* 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__
|
||||
#include <linux/fs.h>
|
||||
|
||||
Reference in New Issue
Block a user