/* line discipline 示例
*
* 目标/场景:
* - 你的 4G LTE 模组通过 USB 枚举为 ttyACM(x),通常默认使用 N_TTY 行规程(用户态 read/poll 循环)
* - 本示例提供一个可加载的内核模块,实现自定义行规程,把收发与缓冲放到内核中:
* - 用户态对 /dev/ttyACM0 的 read/write 将走本行规程的 .read/.write
* - 驱动收到的下行数据通过 .receive_buf(或 .receive_buf2)进入内核环形队列,用户 read 阻塞等待,避免忙轮询
* - 上行数据通过 .write 根据底层 write_room 进行发送,不足时阻塞等待 write_wakeup
*
* 仅包含行规程实现(不创建额外字符设备),按需由用户加载并附着到 tty。
*
* 重要说明:
* - 该实现偏“直通+缓冲”,不做协议解析(比如 AT/PPP 等),你可以在 .receive_buf/.write 中自由插入解析或打包逻辑
* - 行规程编号需要选择一个空闲的 ID,默认使用 29(很多发行版常为空闲),可通过模块参数 ldisc_num=xx 修改
* - 当前系统已注册的行规程可通过:cat /proc/tty/ldiscs 查看
* - 附着到某个 ttyACM 的方法(示例):
* 1) insmod n_lteacm_ldisc.ko ldisc_num=29
* 2) 在用户态用 ioctl(TIOCSETD) 设置行规程号码为 29
* 样例 C 代码:
* int ldisc = 29;
* int fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY);
* ioctl(fd, TIOCSETD, &ldisc);
* 3) 之后对该 fd 的 read/write 将走本行规程;poll/epoll 也可用
* - 卸载模块前,应先确保所有使用该行规程的 tty 已切回其它行规程(例如 N_TTY),否则 tty_unregister_ldisc 会返回 -EBUSY
*
* 兼容性:
* - 主要针对 Linux 4.14+ ~ 6.x
* - 不同内核版本 .receive_buf 与 .receive_buf2 原型略有差异,下面做了简单版本判断
*
* 调试:
* - dmesg 中会打印关键日志
* - 可以通过模块参数开启更详细日志:debug=1
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_ldisc.h>
#include <linux/kfifo.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/version.h>
#include <linux/sockios.h>
#include "asm/string.h"
#include "linux/bpf.h"
#include "lte_driver.h"
#define CLOUD_STATUS_CLIENT_INFO_PATH "/cloud_status/client_info"
#define DMS_MSG_RESERVED 0x20 /* [1, 32]为系统保留消息,不允许用户使用; */
#define MSGID_GLOBAL_BASE (DMS_MSG_RESERVED + 1)
#define DMS_MSG_LOCAL_BASE 0x5001
#define CAP_MID_BASE 0x7000 /* 为避免冲突,CAP子系统消息编码从0x7000开始。 */
#define NSD_MID_BASE 0x5001
#define CLOUD_SDK_MSG_ID (NSD_MID_BASE + 9)
#define DEV_SERVICE_TOKEN_UPDATE_MSG_ID (NSD_MID_BASE + 29)
#define VALIDATE_TOKEN_UPDATE_MSG_ID (NSD_MID_BASE + 28)
#define BC_RING_WIFI_PUSH_MSG_ID (CAP_MID_BASE + 118) /* wifi事件推送消息 */
#define LOW_POWER_CLOUD_MSG_IN_MID (MSGID_GLOBAL_BASE + 41) /* 基础云相关消息 */
#define LTE_DRIVER_MSG_ID (MSGID_GLOBAL_BASE + 128) /* lte driver msg */
#define DMS_MSG_IS_GLOBAL(mid) (((mid) > DMS_MSG_RESERVED) && ((mid) < DMS_MSG_LOCAL_BASE))
#define SHARD_MSG_LEN (8 * 1024)
#define OUT 1
#define IN 0
/* 模块参数:行规程编号、接收缓冲大小、是否打印调试日志 */
static int ldisc_num = 29; /* 默认 29,可通过 insmod n_lteacm_ldisc.ko ldisc_num=xx 指定 */
module_param(ldisc_num, int, 0444);
MODULE_PARM_DESC(ldisc_num, "Line discipline number to register (check /proc/tty/ldiscs)");
static int rx_buf_kb = 4; /* 接收缓冲区大小(KB) */
module_param(rx_buf_kb, int, 0644);
MODULE_PARM_DESC(rx_buf_kb, "RX ring buffer size in KB (default 64KB)");
static int event_rx_buf_cnt = 4; /* event 事件处理队列 */
module_param(event_rx_buf_cnt, int, 0644);
MODULE_PARM_DESC(event_rx_buf_cnt, "IO_BUF_T.cmd_type == BC_CMD_TYPE_EVENT from lte buffer.");
static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Enable debug logs (0=off, 1=on)");
/* 行规程的私有状态 */
struct lte_ldisc {
struct tty_struct *tty;
/* 数据接收缓存,收到完整的IO_BUF_T时拷贝数据到 rx_fifo 或 event_rx_fifio */
IO_BUF_T *io_buf;
int io_buf_recved; /* 已经缓存到io_buf中的字节数 */
spinlock_t io_buf_lock;
/* IO_BUF_T.cmd_type != BC_CMD_TYPE_EVENT 转发到用户空间 */
struct kfifo rx_fifo;
/* IO_BUF_T.cmd_type == BC_CMD_TYPE_EVENT 数据缓存 直接由内核转发 */
struct kfifo event_rx_fifo;
spinlock_t rx_lock; /* 保护 rx_fifo 的自旋锁 */
spinlock_t event_rx_lock; /* 保护 event_rx_fifo 的自旋锁 */
wait_queue_head_t read_wq; /* 用户空间 read 等待队列 */
/* 发送等待(当底层无空间时等待 write_wakeup) */
wait_queue_head_t write_wq;
struct work_struct irq_work; /* 内核对模组事件处理的中断下半部 */
bool rx_overflow_once; /* 仅打印一次溢出提示,避免刷屏 */
/* 状态 */
bool opened;
int cloud_msg_local_len;
unsigned char *cloud_shard_message;
};
/*
* \brief __io_buf_avail, 构建一个完整IO_BUF_T还需要的字节数
*/
static unsigned int __io_buf_avail(struct lte_ldisc *ld)
{
unsigned int result_len = 0;
if(ld->io_buf_recved < MAX_IOCTL_CMD)
{
return sizeof(IO_BUF_T);
}
result_len = MAX_IOCTL_CMD + ld->io_buf->cmd_dlen - ld->io_buf_recved;
if(ld->io_buf_recved > ld->io_buf->cmd_dlen + MAX_IOCTL_CMD)
{
LTE_ERR("[ERROR] dlen:<%d> recv<%d>", ld->io_buf->cmd_dlen, ld->io_buf_recved);
return 0;
}
return MAX_IOCTL_CMD + ld->io_buf->cmd_dlen - ld->io_buf_recved;
}
static int __io_buf_size(struct lte_ldisc *ld)
{
return ld->io_buf_recved;
}
/*
* \param dir, 1: out, 0: in
*
* */
static void print_hex(const unsigned char *data, int len, int dir)
{
int i = 0;
if(NULL == data || 0 == debug)
{
return;
}
if(dir)
{
LTE_INFO("<<[");
}
else
{
LTE_INFO(">>[");
}
for(i = 0; i < len; ++i)
{
if((i % 10) == 0)
{
printk(KERN_INFO "\n");
}
if (data[i] >= 0x20 && data[i] <= 0x7E)
{
// 可打印字符,直接显示字符
printk(KERN_CONT "%c ", data[i]);
}
else
{
// 不可打印字符,显示十六进制
printk(KERN_CONT "%02x ", data[i]);
}
}
LTE_INFO("]");
}
/* 工具函数:安全获取/设置 tty->disc_data */
static inline struct lte_ldisc *ldisc_get(struct tty_struct *tty)
{
return tty ? (struct lte_ldisc *)tty->disc_data : NULL;
}
static inline void ldisc_set(struct tty_struct *tty, struct lte_ldisc *ld)
{
if (tty)
tty->disc_data = ld;
}
static void lte_ldisc_close(struct tty_struct *tty)
{
struct lte_ldisc *ld = ldisc_get(tty);
if (!ld)
return;
ld->opened = false;
/* 唤醒可能阻塞的读写 */
wake_up_interruptible_all(&ld->read_wq);
wake_up_interruptible_all(&ld->write_wq);
/* 释放 FIFO */
kfifo_free(&ld->rx_fifo);
kfifo_free(&ld->event_rx_fifo);
/* 释放 接收缓存 */
kfree(ld->io_buf);
kfree(ld->cloud_shard_message);
ldisc_set(tty, NULL);
kfree(ld);
LTE_INFO("closed on %s\n", tty ? tty_name(tty) : "(null)");
}
static int lte_ldisc_hangup(struct tty_struct *tty)
{
/* 按惯例等价于 close,TTY 层会确保序列 */
lte_ldisc_close(tty);
LTE_INFO("hangup on %s\n", tty ? tty_name(tty) : "(null)");
return 0;
}
/* 注意:底层驱动/软中断调用,务必使用不可睡眠的同步原语(自旋锁等) */
static void lte_driver_recv_irq(struct tty_struct *tty,
const unsigned char *data,
char *fp,
int count)
{
struct lte_ldisc *ld = NULL;
unsigned int need_notify = 0; /* 是否通知应用程序 */
unsigned int need_schedule = 0; /* 是否调度中断下半部 */
unsigned int avail = 0; /* IO_BUF所需空间 */
unsigned int kavail = 0; /* FIFO剩余空间 */
unsigned long io_buf_irqf = 0;
unsigned int io_buf_remain = 0;
if(NULL == tty)
{
LTE_INFO("tty is null.");
return;
}
ld = ldisc_get(tty);
LTE_INFO("<<=========================");
if (!ld || !ld->opened || count <= 0 || !data)
{
LTE_ERR("param check fail.");
return;
}
spin_lock_irqsave(&ld->io_buf_lock, io_buf_irqf);
avail = __io_buf_avail(ld);
if ((unsigned int)count <= avail) /* 未构成一个完整数据包 */
{
LTE_INFO("cp to io_buf: (c:<%d> + r:<%d>)/<%u>", count, ld->io_buf_recved,avail);
memcpy(ld->io_buf + ld->io_buf_recved, data, count);
ld->io_buf_recved += count;
}
else
{
if (avail) /* 构成完整的包 */
{
LTE_WARN("Some Data May Not Trans By IO_BUF_T Nerver Run Here.");
memcpy(ld->io_buf->data + ld->io_buf_recved, data, avail);
ld->io_buf_recved += avail;
io_buf_remain = count - avail;
}
}
avail = __io_buf_avail(ld);
if(0 != avail) /* 未获取到完整的数据包 */
{
LTE_INFO("irq once io_buf: recv:<%d>/allNeed:<%u> avail:<%u>", ld->io_buf_recved, MAX_IOCTL_CMD + ld->io_buf->cmd_dlen, avail);
spin_unlock_irqrestore(&ld->io_buf_lock, io_buf_irqf);
return;
}
/* 分析数据包,根据cmd_type字段放入不同fifo */
if(BC_CMD_TYPE_EVENT == ld->io_buf->cmd_type)
{
/* 只有EVENT字段才需要内核处理 */
LTE_INFO("recv BC_CMD_TYPE_EVENT msg dlen:<%d>!!!!!!!!!!", __io_buf_size(ld) - MAX_IOCTL_CMD);
print_hex(ld->io_buf->cmd_data, ld->io_buf->cmd_dlen, OUT);
spin_lock(&ld->event_rx_lock);
kavail = kfifo_avail(&ld->event_rx_fifo);
if(kavail >= __io_buf_size(ld))
{
LTE_INFO("Msg push to event_rx_fifo");
need_schedule = kfifo_in(&ld->event_rx_fifo, ld->io_buf->data, ld->io_buf_recved);
}
else
{
LTE_WARN("event_rx_fifo size need bigger, data overflow, this package may drop.");
}
spin_unlock(&ld->event_rx_lock);
}
else
{
LTE_INFO("recv msg<%d> len:<%d>", ld->io_buf->cmd_type, __io_buf_size(ld));
print_hex(ld->io_buf->cmd_data, ld->io_buf->cmd_dlen, OUT);
/* 通知应用程序收包 */
spin_lock(&ld->rx_lock);
kavail = kfifo_avail(&ld->rx_fifo);
if(kavail >= __io_buf_size(ld))
{
LTE_INFO("Msg push to rx_fifo");
need_notify = kfifo_in(&ld->rx_fifo, ld->io_buf->data, ld->io_buf_recved);
}
else
{
LTE_WARN("rx_fifo size need bigger, data overflow, this package may drop.");
}
/* 更新 receive_room,给驱动一个“还剩多少余量”的提示 */
tty->receive_room = kfifo_avail(&ld->rx_fifo);
spin_unlock(&ld->rx_lock);
}
memset(ld->io_buf->data, 0, sizeof(IO_BUF_T));
ld->io_buf_recved = 0;
if(io_buf_remain > 0)
{
memcpy(ld->io_buf->data, data + count - io_buf_remain, io_buf_remain);
ld->io_buf_recved = io_buf_remain;
}
spin_unlock_irqrestore(&ld->io_buf_lock, io_buf_irqf);
/* 通知等待进程 */
if (need_notify)
{
wake_up_interruptible(&ld->read_wq);
}
/* 调度工作队列 */
if (need_schedule)
{
schedule_work(&ld->irq_work);
}
}
/* ========== 行规程回调:写可用通知 write_wakeup(底层有空间时调用) ========== */
static void lte_ldisc_write_wakeup(struct tty_struct *tty)
{
struct lte_ldisc *ld = ldisc_get(tty);
if (!ld || !ld->opened)
return;
wake_up_interruptible(&ld->write_wq);
}
extern uint32_t msg_send(uint32_t mid, uint8_t *mbuf, uint32_t mlen);
/*
* \brief lte_msg_send, if(mid > DMS_MSG_RESERVED && mid < DMS_MSG_LOCAL_BASE)
* Send directly.
* else
* Send LTE_DRIVER_MSG_ID msg.
*/
static uint32_t lte_msg_send(uint32_t mid, uint8_t *mbuf, uint32_t mlen)
{
LTE_DRIVER_MSG msg;
if(DMS_MSG_IS_GLOBAL(mid))
{
LTE_INFO("Send global msg:<%u>, len:<%u>", mid, mlen);
return msg_send(mid, mbuf, mlen);
}
msg.mid = mid;
msg.mbuf = mbuf;
msg.mlen = mlen;
LTE_INFO("Send LTE_DRIVER_MSG:<%u>, len:<%u>", mid, mlen);
return msg_send(LTE_DRIVER_MSG_ID, (uint8_t*)&msg, sizeof(LTE_DRIVER_MSG));
}
static int send_to_mqtt_proxy(uint8_t *payload, int payload_len)
{
if (payload == NULL){
return -1;
}
return lte_msg_send(LOW_POWER_CLOUD_MSG_IN_MID, (uint8_t *)payload, payload_len);
}
static void broadcast_cloud_connect_status(int conn_status, int last_conn_status)
{
CLOUD_SDK_MSG conn_status_msg = {0};
memset(&conn_status_msg, 0, sizeof(conn_status_msg));
conn_status_msg.conn_status = conn_status > 0 ? 1 : 0;
conn_status_msg.last_conn_status = last_conn_status;
lte_msg_send(CLOUD_SDK_MSG_ID, (u8 *)&conn_status_msg, sizeof(conn_status_msg));
return;
}
static void lte_ioctl_cb(struct lte_ldisc *ld, IO_BUF_T *cmd_buf)
{
static unsigned char piece = 0;
static unsigned char shard_id = 0;
if(NULL == ld || NULL == cmd_buf)
{
return;
}
switch (cmd_buf->cmd_type)
{
case BC_CMD_TYPE_PUBLISH:
case BC_CMD_TYPE_GET:
case BC_CMD_TYPE_SET:
case BC_CMD_TYPE_ACK:
LTE_WARN("Message:<%d> distribution error.", cmd_buf->cmd_type);
break;
case BC_CMD_TYPE_EVENT:
LTE_INFO("recv event name:%d, dlen:%d\n", cmd_buf->cmd_name, cmd_buf->cmd_dlen);
print_hex(cmd_buf->cmd_data, cmd_buf->cmd_dlen, OUT);
switch(cmd_buf->cmd_name)
{
case BC_EVENT_CLOUD_CONNECT:
LTE_INFO(">>>>>>>>>>>>>>>>>> connect cloud connect <<<<<<<<<<<<<<<<<<\n");
broadcast_cloud_connect_status(1, -1);
break;
case BC_EVENT_CLOUD_DISCONNECT:
broadcast_cloud_connect_status(0, -1);
break;
case BC_EVENT_VALIDATE_TOKEN_UPDATE:
lte_msg_send(VALIDATE_TOKEN_UPDATE_MSG_ID, NULL, 0);
break;
case BC_EVENT_DEV_TOKEN_UPDATE:
lte_msg_send(DEV_SERVICE_TOKEN_UPDATE_MSG_ID, NULL, 0);
break;
case BC_EVENT_IOT_CHIP_LOG:
LTE_INFO("SHIP2LOG %s\n", cmd_buf->cmd_data + 1);
break;
case BC_EVENT_RING_MSG:
lte_msg_send(BC_RING_WIFI_PUSH_MSG_ID, cmd_buf->cmd_data + 1, cmd_buf->cmd_dlen - 1);
break;
#ifdef BATTERY_STATISTIC_SUPPORT
case BC_EVENT_BAT_DATA_PREPARE:
LTE_INFO("date ok,get bat_stat_data");
send_bat_stat_data();
break;
#endif
case BC_EVENT_CLOUD_MESSAGE:
// cmd_data首字节是事件计数,实际长度是cmd_dlen - 1
LTE_ERR("CLOUD MSG EVENT, len=%d\n", cmd_buf->cmd_dlen - 1);
if (cmd_buf->cmd_dlen - 1 < (sizeof(cmd_buf->cmd_data) - 1))
{
cmd_buf->cmd_data[cmd_buf->cmd_dlen] = 0;
send_to_mqtt_proxy(&cmd_buf->cmd_data[1], cmd_buf->cmd_dlen);
} else {
LTE_ERR("buf->cmd_dlen too long:%d", cmd_buf->cmd_dlen);
send_to_mqtt_proxy(&cmd_buf->cmd_data[1], cmd_buf->cmd_dlen - 1);
}
break;
// TODO: 补充其他事件处理
case BC_EVENT_CLOUD_MESSAGE_PORTION:
if (piece == 0)
{
shard_id = cmd_buf->cmd_data[2];
}
if ((piece == cmd_buf->cmd_data[1]) && (ld->cloud_msg_local_len + cmd_buf->cmd_dlen - 3 <= SHARD_MSG_LEN) && (shard_id == cmd_buf->cmd_data[2])) {
memcpy(ld->cloud_shard_message + ld->cloud_msg_local_len, &cmd_buf->cmd_data[3], cmd_buf->cmd_dlen - 3);
ld->cloud_msg_local_len += cmd_buf->cmd_dlen - 3;
piece++;
} else {
piece = 0;
ld->cloud_msg_local_len = 0;
memset(ld->cloud_shard_message, 0, SHARD_MSG_LEN);
}
break;
case BC_EVENT_CLOUD_MESSAGE_FINAL:
if ((piece == cmd_buf->cmd_data[1]) && (ld->cloud_msg_local_len + cmd_buf->cmd_dlen - 2 <= SHARD_MSG_LEN) && (shard_id == cmd_buf->cmd_data[2])) {
memcpy(ld->cloud_shard_message + ld->cloud_msg_local_len, &cmd_buf->cmd_data[3], cmd_buf->cmd_dlen - 3);
ld->cloud_msg_local_len += cmd_buf->cmd_dlen - 3;
send_to_mqtt_proxy(ld->cloud_shard_message, ld->cloud_msg_local_len);
}
piece = 0;
ld->cloud_msg_local_len = 0;
memset(ld->cloud_shard_message, 0, SHARD_MSG_LEN);
break;
case BC_EVENT_WIFI_WATCHDOG:
LTE_INFO("[CB]WIFI WATCHDOG EVENT :%d\n", cmd_buf->cmd_data[1]);
//add wdt?
break;
default:
break;
}
break;
default:
LTE_ERR("unknown type:%d cmd:%d\n", cmd_buf->cmd_type, cmd_buf->cmd_name);
break;
}
}
static void irq_work_handler(struct work_struct *work)
{
struct lte_ldisc *ld = container_of(work, struct lte_ldisc, irq_work);
unsigned long event_rx_buf_irqf = 0;
int fifo_len = 0, read_len;
IO_BUF_T io_buf;
memset(&io_buf, 0, sizeof(IO_BUF_T));
LTE_INFO("handler irq");
spin_lock_irqsave(&ld->event_rx_lock, event_rx_buf_irqf);
fifo_len = kfifo_len(&ld->event_rx_fifo);
if(0 == fifo_len || fifo_len < MAX_IOCTL_CMD)
{
LTE_WARN("event_rx_fifo empty but wake up handler.");
return;
}
read_len = kfifo_out(&ld->event_rx_fifo, &io_buf.data, MAX_IOCTL_CMD);
if(io_buf.cmd_dlen > kfifo_len(&ld->event_rx_fifo))
{
LTE_WARN("event_rx_fifo data unparse fail.");
kfifo_reset(&ld->event_rx_fifo);
return;
}
kfifo_out(&ld->event_rx_fifo, &io_buf.cmd_data, io_buf.cmd_dlen);
spin_unlock_irqrestore(&ld->event_rx_lock, event_rx_buf_irqf);
/* 开始协议解析部分 */
lte_ioctl_cb(ld, &io_buf);
}
static ssize_t lte_ioctl_read(struct tty_struct *tty, unsigned char __user *buf)
{
struct lte_ldisc *ld = ldisc_get(tty);
unsigned int copied_total = 0;
IO_BUF_T cmd_header;
unsigned int fifo_len = 0;
unsigned long irqf = 0;
int ret = 0;
if (!ld || !ld->opened)
{
LTE_ERR("param check fail.");
return -EIO;
}
LTE_INFO("read data from ioctl cb.");
spin_lock_irqsave(&ld->rx_lock, irqf);
fifo_len = kfifo_len(&ld->rx_fifo);
if(fifo_len < MAX_IOCTL_CMD)
{
spin_unlock_irqrestore(&ld->rx_lock, irqf);
/* 阻塞等待 receive_buf 唤醒,期间可被信号中断 */
ret = wait_event_interruptible(ld->read_wq, ({
unsigned int l;
unsigned long f;
spin_lock_irqsave(&ld->rx_lock, f);
l = kfifo_len(&ld->rx_fifo);
spin_unlock_irqrestore(&ld->rx_lock, f);
l >= MAX_IOCTL_CMD || !ld->opened;
}));
if (ret)
return -1;
if (!ld->opened) {
return -EIO;
}
spin_lock_irqsave(&ld->rx_lock, irqf);
}
fifo_len = kfifo_len(&ld->rx_fifo);
if(fifo_len < MAX_IOCTL_CMD)
{
spin_unlock_irqrestore(&ld->rx_lock, irqf);
LTE_ERR("False wake-up");
return -1;
}
memset(&cmd_header, 0, sizeof(IO_BUF_T));
kfifo_out_peek(&ld->rx_fifo, cmd_header.data, MAX_IOCTL_CMD);
if(fifo_len < cmd_header.cmd_dlen + MAX_IOCTL_CMD)
{
spin_unlock_irqrestore(&ld->rx_lock, irqf);
LTE_ERR("False wake-up");
return -1;
}
kfifo_out(&ld->rx_fifo, cmd_header.data, MAX_IOCTL_CMD + cmd_header.cmd_dlen);
copy_to_user(buf, cmd_header.data, MAX_IOCTL_CMD + cmd_header.cmd_dlen);
tty->receive_room = kfifo_avail(&ld->rx_fifo);
spin_unlock_irqrestore(&ld->rx_lock, irqf);
LTE_INFO("read <%d> bytes success.", MAX_IOCTL_CMD + cmd_header.cmd_dlen);
return copied_total;
}
/* ========== 行规程回调:用户态 read/write/poll ========== */
static ssize_t lte_ldisc_read(struct tty_struct *tty, struct file *file,
unsigned char *buf, size_t nr, void **cookie, unsigned long offset)
{
struct lte_ldisc *ld = ldisc_get(tty);
ssize_t copied_total = 0;
int ret = 0;
LTE_ERR("Warning This method should not be Called.");
if (!ld || !ld->opened)
{
LTE_ERR("param check fail.");
return -EIO;
}
if (nr == 0)
{
return 0;
}
/* 循环直到满足用户请求或遇到阻塞条件 */
while (nr) {
size_t to_copy;
unsigned int fifo_len;
unsigned long irqf;
unsigned char *tmp;
/* 尝试从 FIFO 拿数据,如果没有则根据 O_NONBLOCK 决定是否睡眠等待 */
spin_lock_irqsave(&ld->rx_lock, irqf);
fifo_len = kfifo_len(&ld->rx_fifo);
spin_unlock_irqrestore(&ld->rx_lock, irqf);
if (fifo_len == 0)
{
/* 若已有部分数据,先返回(避免读到部分就阻塞) */
if (copied_total > 0)
break;
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
/* 阻塞等待 receive_buf 唤醒,期间可被信号中断 */
ret = wait_event_interruptible(ld->read_wq, ({
unsigned int l;
unsigned long f;
spin_lock_irqsave(&ld->rx_lock, f);
l = kfifo_len(&ld->rx_fifo);
spin_unlock_irqrestore(&ld->rx_lock, f);
l > 0 || !ld->opened;
}));
if (ret)
break; /* -ERESTARTSYS 等,用户态可重试 */
if (!ld->opened) {
ret = -EIO;
break;
}
continue;
}
/* 本次最多读取 nr 与 fifo_len 中较小的数量 */
to_copy = min_t(size_t, nr, fifo_len);
/* 为了避免在持有自旋锁时 copy_to_user(可能睡眠),先把数据出队到临时缓冲 */
tmp = kmalloc(to_copy, GFP_KERNEL);
if (!tmp) {
ret = -ENOMEM;
break;
}
spin_lock_irqsave(&ld->rx_lock, irqf);
to_copy = kfifo_out(&ld->rx_fifo, tmp, to_copy);
tty->receive_room = kfifo_avail(&ld->rx_fifo);
spin_unlock_irqrestore(&ld->rx_lock, irqf);
/* 复制到用户空间 */
memcpy(buf, tmp, to_copy);
print_hex(tmp, to_copy, OUT);
kfree(tmp);
buf += to_copy;
nr -= to_copy;
copied_total += to_copy;
/* 一次读够了,返回 */
if (nr == 0)
break;
/* FIFO 可能还有数据,继续下一轮;也可能为空,需要等待 */
}
/* 若已读出部分数据,优先返回字节数;否则返回错误码 */
return (copied_total > 0) ? copied_total : ret;
}
static ssize_t lte_ldisc_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
struct lte_ldisc *ld = NULL;
ssize_t written_total = 0;
unsigned char *kbuf = NULL;
int ret = 0;
if(NULL == tty)
{
LTE_ERR("tty is null.");
return -ENOENT;
}
ld = ldisc_get(tty);
if(NULL == ld)
{
LTE_ERR("tty is not set lte_ldisc private.");
return -1;
}
if(buf == NULL)
{
LTE_ERR("user buf is null.");
return -1;
}
LTE_INFO("Call lte_ldisc_write with: nr<%u>", nr);
if (!ld || !ld->opened)
{
LTE_ERR("param check fail.");
return -EIO;
}
if (nr == 0)
return 0;
kbuf = kmalloc(nr, GFP_KERNEL);
if (!kbuf)
{
LTE_ERR("kbuf malloc error request:<%d> bytes.", nr);
return -ENOMEM;
}
memcpy(kbuf, buf, nr);
LTE_INFO("Write Bytes:<%d>", nr);
print_hex(kbuf, nr, IN);
while (written_total < nr)
{
int room = 0;
int to_send = 0;
int sent = 0;
/* 查询底层可写空间 */
room = tty_write_room(tty);
LTE_INFO("tty_write_room: %d.", room);
if (room <= 0) {
LTE_WARN("Write Buffer is empty!!!");
/* 若已写出部分,先返回;否则根据是否非阻塞确定是否等待 */
if (written_total > 0)
{
break;
}
if (file->f_flags & O_NONBLOCK) {
LTE_INFO("O_NONBLOCK EAGAIN.");
ret = -EAGAIN;
break;
}
/* 阻塞等待底层 write_wakeup */
ret = wait_event_interruptible(ld->write_wq, ({
int r = tty_write_room(tty);
r > 0 || !ld->opened;
}));
if (ret)
break;
if (!ld->opened)
{
LTE_ERR("Write but tty is closed.");
ret = -EIO;
break;
}
continue;
}
/* 按底层空间发送 */
to_send = min_t(int, room, (int)(nr - written_total));
sent = tty->ops->write(tty, kbuf + written_total, to_send);
if (sent < 0)
{
LTE_ERR("tty write fail with:<%d>", ret);
ret = sent;
break;
}
else if (sent == 0)
{
LTE_WARN("tty write return 0");
/* 某些驱动可能短暂返回 0,避免忙等稍作让步或等待 */
if (file->f_flags & O_NONBLOCK)
{
ret = (written_total > 0) ? written_total : -EAGAIN;
break;
}
/* 轻微让步,或等待 write_wakeup */
schedule_timeout_interruptible(msecs_to_jiffies(5));
continue;
}
written_total += sent;
}
kfree(kbuf);
LTE_INFO("write finished, write total:<%d> ret:<%d>", written_total, ret);
return (written_total > 0) ? written_total : ret;
}
static __poll_t lte_ldisc_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
{
struct lte_ldisc *ld = ldisc_get(tty);
__poll_t mask = 0;
unsigned long irqf;
unsigned int fifo_len;
if (!ld || !ld->opened)
return EPOLLERR | EPOLLHUP;
/* 将等待队列加入到轮询表中,以支持 select/poll/epoll */
poll_wait(file, &ld->read_wq, wait);
poll_wait(file, &ld->write_wq, wait);
/* 可读? */
spin_lock_irqsave(&ld->rx_lock, irqf);
fifo_len = kfifo_len(&ld->rx_fifo);
spin_unlock_irqrestore(&ld->rx_lock, irqf);
if (fifo_len > 0)
mask |= EPOLLIN | EPOLLRDNORM;
/* 可写? */
if (tty_write_room(tty) > 0)
{
mask |= EPOLLOUT | EPOLLWRNORM;
}
return mask;
}
static int lte_ldisc_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
int ret = -1;
IO_BUF_T tmp_buffer;
/* 透传未知命令给用户态(返回 -ENOIOCTLCMD 让 TTY 栈尝试其它处理) */
LTE_INFO("lte_ldisc_ioctl called cmd:<%u>", cmd);
switch(cmd)
{
case SIOCDEVPRIVATE:
memset(&tmp_buffer, 0, sizeof(IO_BUF_T));
if(copy_from_user(&tmp_buffer.data, (void*)arg, MAX_IOCTL_CMD))
{
LTE_ERR("ioctl user buffer can't access.");
break;
}
if(copy_from_user(&tmp_buffer.cmd_data, (char*)arg + MAX_IOCTL_CMD, tmp_buffer.cmd_dlen))
{
LTE_ERR("ioctl user buffer can't access.");
break;
}
LTE_INFO("receive ioctl cmd_type:<%d> cmd_name:<%d> cmd_length:<%u> cmd_data:", tmp_buffer.cmd_type, tmp_buffer.cmd_name, tmp_buffer.cmd_dlen);
ret = lte_ldisc_write(tty, file, (unsigned char*)&tmp_buffer.data, tmp_buffer.cmd_dlen + MAX_IOCTL_CMD);
break;
default:
LTE_INFO("lte_ldisc not support cmd:<%u>.", cmd);
ret = -ENOIOCTLCMD;
break;
}
ret = lte_ioctl_read(tty, (void*)arg);
LTE_INFO("lte_ioctl_read ret:<%d>", ret);
return ret >= 0 ? 0 : -1;
}
static int lte_ldisc_open(struct tty_struct *tty)
{
struct lte_ldisc *ld = NULL;
int ret = -EINVAL;
LTE_INFO("open tty <%s>", tty_name(tty));
if (!tty)
{
LTE_ERR("tty is null.");
return -EINVAL;
}
ld = kzalloc(sizeof(*ld), GFP_KERNEL);
if (!ld)
{
LTE_ERR("kzalloc empty.");
return -ENOMEM;
}
/* 分配接收缓存 */
ld->io_buf = kzalloc(sizeof(IO_BUF_T), GFP_KERNEL);
if(NULL == ld->io_buf)
{
LTE_ERR("kzalloc io_buf empty.");
goto err_rx_fifo;
}
ld->tty = tty;
spin_lock_init(&ld->rx_lock);
spin_lock_init(&ld->event_rx_lock);
spin_lock_init(&ld->io_buf_lock);
init_waitqueue_head(&ld->read_wq);
init_waitqueue_head(&ld->write_wq);
INIT_WORK(&ld->irq_work, irq_work_handler);
/* 分配接收环形缓冲区 */
ret = kfifo_alloc(&ld->rx_fifo, rx_buf_kb * 1024, GFP_KERNEL);
if (ret)
{
LTE_ERR("kfifo_alloc(%dKB) failed: %d\n", rx_buf_kb, ret);
goto err_rx_fifo;
}
ret = kfifo_alloc(&ld->event_rx_fifo, event_rx_buf_cnt * sizeof(IO_BUF_T), GFP_KERNEL);
if (ret)
{
LTE_ERR("kfifo_alloc(%dKB) failed: %d\n", event_rx_buf_cnt * sizeof(IO_BUF_T), ret);
goto err_event_tx_fifo;
}
ld->cloud_shard_message = kzalloc(SHARD_MSG_LEN, GFP_KERNEL);
if(!ld->cloud_shard_message)
{
LTE_ERR("alloc cloud_shard_message fail.");
goto err_cloud_shard_message;
}
/* 告诉底层驱动我们目前能接收的空间 */
tty->receive_room = kfifo_avail(&ld->rx_fifo);
ldisc_set(tty, ld);
ld->opened = true;
LTE_INFO("opened on %s (RX %dKB), (TX %dKB), ldisc=%d\n",
tty_name(tty), rx_buf_kb, event_rx_buf_cnt * sizeof(IO_BUF_T), ldisc_num);
return 0;
err_cloud_shard_message:
kfifo_free(&ld->event_rx_fifo);
err_event_tx_fifo:
kfifo_free(&ld->rx_fifo);
err_rx_fifo:
kfree(ld);
ld = NULL;
return ret;
}
/* ========== 行规程 ops 描述符 ========== */
static struct tty_ldisc_ops lte_ldisc_ops = {
.owner = THIS_MODULE,
.name = "n_lteacm",
.open = lte_ldisc_open,
.close = lte_ldisc_close,
.hangup = lte_ldisc_hangup,
.read = lte_ldisc_read,
.write = lte_ldisc_write,
.poll = lte_ldisc_poll,
.ioctl = lte_ldisc_ioctl,
.receive_buf = lte_driver_recv_irq,
.write_wakeup = lte_ldisc_write_wakeup,
/* 暂时不处理这些接口
* .set_termios = NULL,
* .flush_buffer = NULL,
* .throttle = NULL,
* .unthrottle = NULL,
*/
};
/* ========== 模块加载/卸载 ========== */
static int __init lte_ldisc_init(void)
{
int ret = -EINVAL;
if (ldisc_num < 0 || ldisc_num >= NR_LDISCS) {
LTE_ERR("invalid ldisc_num=%d, valid range [0, %d)\n", ldisc_num, NR_LDISCS);
return -EINVAL;
}
ret = tty_register_ldisc(ldisc_num, <e_ldisc_ops);
if (ret)
{
LTE_ERR("tty_register_ldisc(%d) failed: %d\n", ldisc_num, ret);
return ret;
}
LTE_INFO("registered ldisc=%d, RX=%dKB, debug=%d, version=%s\n",
ldisc_num, rx_buf_kb, debug, DRV_VERSION);
LTE_INFO("Attach with TIOCSETD(%d) on your /dev/ttyACM*\n", ldisc_num);
return 0;
}
static void __exit lte_ldisc_exit(void)
{
int ret = 0;
ret = tty_unregister_ldisc(ldisc_num);
if (ret) {
/* 若返回 -EBUSY,说明仍有 tty 使用该行规程,需先切回其它行规程再卸载模块 */
LTE_ERR("tty_unregister_ldisc(%d) failed: %d (busy?)\n", ldisc_num, ret);
/* 通常此时 rmmod 会失败,因为模块被引用;用户需先 detach 然后重试卸载 */
} else {
LTE_INFO("unregistered ldisc=%d\n", ldisc_num);
}
}
module_init(lte_ldisc_init);
module_exit(lte_ldisc_exit);
MODULE_AUTHOR("liujiarui <liujiarui@tp-link.com.hk>");
MODULE_DESCRIPTION(DRV_DESC);
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL");
这段代码中可能到只Kernel panic的地方
最新发布