unplugged-kernel/drivers/misc/mediatek/eccci/port/port_char.c

221 lines
5.6 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/poll.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif
#include "ccci_config.h"
#include "ccci_common_config.h"
#include "ccci_bm.h"
#include "port_proxy.h"
#include "port_char.h"
#include "port_smem.h"
#include "ccci_fsm.h"
#include "ccci_ipc_task_ID.h"
#define MAX_QUEUE_LENGTH 32
unsigned int port_char_dev_poll(struct file *fp,
struct poll_table_struct *poll)
{
struct port_t *port = fp->private_data;
unsigned int mask = 0;
int md_id = port->md_id;
int md_state = ccci_fsm_get_md_state(md_id);
CCCI_DEBUG_LOG(md_id, CHAR, "poll on %s\n", port->name);
poll_wait(fp, &port->rx_wq, poll);
/* TODO: lack of poll wait for Tx */
if (!skb_queue_empty(&port->rx_skb_list))
mask |= POLLIN | POLLRDNORM;
if (port_write_room_to_md(port) > 0)
mask |= POLLOUT | POLLWRNORM;
if (port->rx_ch == CCCI_UART1_RX &&
md_state != READY &&
md_state != EXCEPTION) {
/* notify MD logger to save its log
* before md_init kills it
*/
mask |= POLLERR;
CCCI_NORMAL_LOG(md_id, CHAR,
"poll error for MD logger at state %d,mask=%d\n",
md_state, mask);
}
return mask;
}
static const struct file_operations char_dev_fops = {
.owner = THIS_MODULE,
.open = &port_dev_open, /*use default API*/
.read = &port_dev_read, /*use default API*/
.write = &port_dev_write, /*use default API*/
.release = &port_dev_close,/*use default API*/
.unlocked_ioctl = &port_dev_ioctl,/*use default API*/
#ifdef CONFIG_COMPAT
.compat_ioctl = &port_dev_compat_ioctl,/*use default API*/
#endif
.poll = &port_char_dev_poll,/*use port char self API*/
.mmap = &port_dev_mmap,
};
static int port_char_init(struct port_t *port)
{
struct cdev *dev = NULL;
int ret = 0;
int md_id = port->md_id;
CCCI_DEBUG_LOG(md_id, CHAR,
"char port %s is initializing\n", port->name);
port->rx_length_th = MAX_QUEUE_LENGTH;
port->skb_from_pool = 1;
port->interception = 0;
if (port->flags & PORT_F_WITH_CHAR_NODE) {
dev = kmalloc(sizeof(struct cdev), GFP_KERNEL);
if (unlikely(!dev)) {
CCCI_ERROR_LOG(port->md_id, CHAR,
"alloc char dev fail!!\n");
return -1;
}
cdev_init(dev, &char_dev_fops);
dev->owner = THIS_MODULE;
ret = cdev_add(dev, MKDEV(port->major,
port->minor_base + port->minor), 1);
ret = ccci_register_dev_node(port->name, port->major,
port->minor_base + port->minor);
port->flags |= PORT_F_ADJUST_HEADER;
}
#ifndef DPMAIF_DEBUG_LOG
if (port->rx_ch == CCCI_UART2_RX ||
port->rx_ch == CCCI_C2K_AT ||
port->rx_ch == CCCI_C2K_AT2 ||
port->rx_ch == CCCI_C2K_AT3 ||
port->rx_ch == CCCI_C2K_AT4 ||
port->rx_ch == CCCI_C2K_AT5 ||
port->rx_ch == CCCI_C2K_AT6 ||
port->rx_ch == CCCI_C2K_AT7 ||
port->rx_ch == CCCI_C2K_AT8)
port->flags |= PORT_F_CH_TRAFFIC;
else if (port->rx_ch == CCCI_FS_RX)
port->flags |= (PORT_F_CH_TRAFFIC | PORT_F_DUMP_RAW_DATA);
#endif
return ret;
}
#ifdef CONFIG_MTK_ECCCI_C2K
static int c2k_req_push_to_usb(struct port_t *port, struct sk_buff *skb)
{
int md_id = port->md_id;
struct ccci_header *ccci_h = NULL;
int read_len, read_count, ret = 0;
int c2k_ch_id;
#if (MD_GENERATION <= 6292)
int ppp_rx_ch = CCCI_C2K_PPP_DATA;
#else
int ppp_rx_ch = CCCI_C2K_PPP_RX;
#endif
if (port->rx_ch == ppp_rx_ch)
c2k_ch_id = DATA_PPP_CH_C2K-1;
else if (port->rx_ch == CCCI_MD_LOG_RX)
c2k_ch_id = MDLOG_CH_C2K-2;
else {
ret = -ENODEV;
CCCI_ERROR_LOG(md_id, CHAR,
"Err: wrong ch_id(%d) from usb bypass\n", port->rx_ch);
return ret;
}
/* calculate available data */
ccci_h = (struct ccci_header *)skb->data;
read_len = skb->len - sizeof(struct ccci_header);
/* remove CCCI header */
skb_pull(skb, sizeof(struct ccci_header));
retry_push:
/* push to usb */
read_count = rawbulk_push_upstream_buffer(c2k_ch_id,
skb->data, read_len);
CCCI_DEBUG_LOG(md_id, CHAR,
"data push to usb bypass (ch%d)(%d)\n",
port->rx_ch, read_count);
if (read_count > 0) {
skb_pull(skb, read_count);
read_len -= read_count;
if (read_len > 0)
goto retry_push;
else if (read_len == 0)
ccci_free_skb(skb);
else if (read_len < 0)
CCCI_ERROR_LOG(md_id, CHAR,
"read_len error, check why come here\n");
} else {
CCCI_NORMAL_LOG(md_id, CHAR, "usb buf full\n");
msleep(20);
goto retry_push;
}
return read_len;
}
#endif
static int port_char_recv_skb(struct port_t *port, struct sk_buff *skb)
{
int md_id = port->md_id;
if (!atomic_read(&port->usage_cnt) &&
(port->rx_ch != CCCI_UART2_RX &&
port->rx_ch != CCCI_C2K_AT &&
port->rx_ch != CCCI_PCM_RX &&
port->rx_ch != CCCI_FS_RX &&
port->rx_ch != CCCI_RPC_RX &&
port->rx_ch != CCCI_UDC_RX &&
!(port->rx_ch == CCCI_IPC_RX &&
port->minor ==
AP_IPC_LWAPROXY + CCCI_IPC_MINOR_BASE)))
return -CCCI_ERR_DROP_PACKET;
#ifdef CONFIG_MTK_ECCCI_C2K
if (port->interception) {
c2k_req_push_to_usb(port, skb);
return 0;
}
#endif
CCCI_DEBUG_LOG(md_id, CHAR, "recv on %s, len=%d\n",
port->name, port->rx_skb_list.qlen);
return port_recv_skb(port, skb);
}
void port_char_dump_info(struct port_t *port, unsigned int flag)
{
if (port == NULL) {
CCCI_ERROR_LOG(0, CHAR, "%s: port==NULL\n", __func__);
return;
}
if (atomic_read(&port->usage_cnt) == 0)
return;
if (port->flags & PORT_F_CH_TRAFFIC)
CCCI_REPEAT_LOG(port->md_id, CHAR,
"CHR:(%d):%dR(%d,%d,%d):%dT(%d)\n",
port->flags, port->rx_ch,
port->rx_skb_list.qlen,
port->rx_pkg_cnt, port->rx_drop_cnt,
port->tx_ch, port->tx_pkg_cnt);
}
struct port_ops char_port_ops = {
.init = &port_char_init,
.recv_skb = &port_char_recv_skb,
.dump_info = &port_char_dump_info,
};