201 lines
4.7 KiB
C
201 lines
4.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2015 MediaTek Inc.
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#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 <linux/module.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_fdt.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/of_address.h>
|
|
#include "ccci_config.h"
|
|
#include "mt-plat/mtk_ccci_common.h"
|
|
|
|
#ifdef FEATURE_RF_CLK_BUF
|
|
#include <mtk_clkbuf_ctl.h>
|
|
#endif
|
|
|
|
#include "ccci_core.h"
|
|
#include "ccci_bm.h"
|
|
#include "ccci_modem.h"
|
|
#include "port_misc.h"
|
|
|
|
#define MAX_QUEUE_LENGTH 16
|
|
|
|
struct ccci_misc_cb_func_info ccci_misc_cb_table[MAX_MD_NUM][ID_USER_MAX];
|
|
|
|
int register_ccci_func_call_back(int md_id, unsigned int id,
|
|
ccci_misc_cb_func_t func)
|
|
{
|
|
int ret = 0;
|
|
struct ccci_misc_cb_func_info *info_ptr;
|
|
|
|
if (md_id >= MAX_MD_NUM) {
|
|
CCCI_ERROR_LOG(md_id, MISC,
|
|
"register_sys_call_back fail: invalid md id\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
info_ptr = &(ccci_misc_cb_table[md_id][id]);
|
|
|
|
if (info_ptr->func == NULL) {
|
|
info_ptr->id = id;
|
|
info_ptr->func = func;
|
|
} else {
|
|
CCCI_ERROR_LOG(md_id, MISC,
|
|
"register_ccci_misc_call_back fail: func(0x%x) registered! %ps\n",
|
|
id, info_ptr->func);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void exec_ccci_misc_call_back(int md_id, int cb_id, void *data, int data_len)
|
|
{
|
|
ccci_misc_cb_func_t func;
|
|
int id;
|
|
struct ccci_misc_cb_func_info *curr_table;
|
|
|
|
if (md_id >= MAX_MD_NUM) {
|
|
CCCI_ERROR_LOG(md_id, MISC,
|
|
"exec_ccci_misc_cb fail: invalid md id\n");
|
|
return;
|
|
}
|
|
|
|
id = cb_id & 0xFF;
|
|
if (id >= ID_USER_MAX) {
|
|
CCCI_ERROR_LOG(md_id, MISC,
|
|
"exec_sys_cb fail: invalid func id(0x%x)\n", cb_id);
|
|
return;
|
|
}
|
|
|
|
curr_table = ccci_misc_cb_table[md_id];
|
|
|
|
func = curr_table[id].func;
|
|
if (func != NULL)
|
|
func(md_id, data, data_len);
|
|
else
|
|
CCCI_ERROR_LOG(md_id, MISC,
|
|
"exec_ccci_misc_cb fail: func id(0x%x) not register!\n",
|
|
cb_id);
|
|
}
|
|
|
|
/*
|
|
* define character device operation for misc_u
|
|
*/
|
|
static const struct file_operations ccci_misc_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*/
|
|
};
|
|
|
|
static int port_misc_kernel_thread(void *arg)
|
|
{
|
|
struct port_t *port = arg;
|
|
struct sk_buff *skb;
|
|
struct ccci_header *ccci_h;
|
|
unsigned long flags;
|
|
int ret = 0;
|
|
/* struct rmmi_camera_arfcn_info_struct *recv_msg; */
|
|
|
|
CCCI_DEBUG_LOG(port->md_id, MISC,
|
|
"port %s's thread running\n", port->name);
|
|
|
|
while (1) {
|
|
retry:
|
|
if (skb_queue_empty(&port->rx_skb_list)) {
|
|
ret = wait_event_interruptible(port->rx_wq,
|
|
!skb_queue_empty(&port->rx_skb_list));
|
|
if (ret == -ERESTARTSYS)
|
|
continue; /* FIXME */
|
|
}
|
|
if (kthread_should_stop())
|
|
break;
|
|
CCCI_DEBUG_LOG(port->md_id, MISC,
|
|
"read on %s\n", port->name);
|
|
/* 1. dequeue */
|
|
spin_lock_irqsave(&port->rx_skb_list.lock, flags);
|
|
skb = __skb_dequeue(&port->rx_skb_list);
|
|
if (port->rx_skb_list.qlen == 0)
|
|
port_ask_more_req_to_md(port);
|
|
spin_unlock_irqrestore(&port->rx_skb_list.lock, flags);
|
|
if (skb == NULL)
|
|
goto retry;
|
|
/* 2. process the request */
|
|
/* ccci header */
|
|
ccci_h = (struct ccci_header *)skb->data;
|
|
skb_pull(skb, sizeof(struct ccci_header));
|
|
|
|
switch (ccci_h->channel) {
|
|
case CCCI_MIPI_CHANNEL_RX:
|
|
exec_ccci_misc_call_back(port->md_id,
|
|
ID_MD_CAMERA, skb->data, skb->len);
|
|
break;
|
|
default:
|
|
CCCI_ERROR_LOG(port->md_id, MISC,
|
|
"recv unknown channel %d\n",
|
|
ccci_h->channel);
|
|
break;
|
|
}
|
|
CCCI_DEBUG_LOG(port->md_id, MISC,
|
|
"read done on %s\n",
|
|
port->name);
|
|
ccci_free_skb(skb);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int port_misc_init(struct port_t *port)
|
|
{
|
|
struct cdev *dev;
|
|
int ret = 0;
|
|
|
|
CCCI_DEBUG_LOG(port->md_id, MISC,
|
|
"ccci misc 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, MISC,
|
|
"alloc misc char dev fail!!\n");
|
|
return -1;
|
|
}
|
|
cdev_init(dev, &ccci_misc_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;
|
|
} else {
|
|
kthread_run(port_misc_kernel_thread, port, "%s", port->name);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct port_ops ccci_misc_port_ops = {
|
|
.init = &port_misc_init,
|
|
.recv_skb = &port_recv_skb,
|
|
};
|
|
|