mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-14 08:36:12 -04:00
mmi_muc_uart: switch from tty to char dev
Switch from a tty interface to a char device so that we can implement message based reads instead of a stream. Change-Id: I23e7cb9bedcb51d857c3159b64b81f30574c132d Signed-off-by: Ryan Lattrel <ryanl@motorola.com> Reviewed-on: https://gerrit.mot.com/1238344 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Reviewed-by: Master Yoda Tested-by: Jira Key Reviewed-by: Kenneth Kessler <kennykessler@motorola.com> Submit-Approved: Jira Key
This commit is contained in:
parent
1d1e2644b5
commit
9b887b876c
5 changed files with 359 additions and 41 deletions
|
|
@ -3,6 +3,6 @@ EXTRA_CFLAGS += -Wall
|
|||
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
|
||||
|
||||
mmi_muc_uart-objs += mmi_uart.o
|
||||
mmi_muc_uart-objs += mmi_tty.o
|
||||
mmi_muc_uart-objs += mmi_char.o
|
||||
mmi_muc_uart-objs += muc_uart.o
|
||||
obj-m += mmi_muc_uart.o
|
||||
145
drivers/mmi_muc_uart/mmi_char.c
Normal file
145
drivers/mmi_muc_uart/mmi_char.c
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/* Copyright (c) 2018, Motorola Mobility LLC. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/platform_device.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include "mmi_char.h"
|
||||
#include "muc_uart.h"
|
||||
|
||||
#define NUM_MINORS 1
|
||||
|
||||
static struct platform_device *parent_pdev;
|
||||
static struct cdev mmi_char_cdev;
|
||||
static struct class *mmi_char_class;
|
||||
static dev_t mmi_char_dev_num;
|
||||
wait_queue_head_t mmi_char_read_wq;
|
||||
static int mmi_char_major;
|
||||
static int mmi_char_max_write_size;
|
||||
static atomic_t mmi_char_open_excl;
|
||||
|
||||
static int mmi_char_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (atomic_xchg(&mmi_char_open_excl, 1))
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmi_char_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
WARN_ON(!atomic_xchg(&mmi_char_open_excl, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t mmi_char_write(struct file *file,
|
||||
const char __user *buf,
|
||||
size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
if(count > mmi_char_max_write_size)
|
||||
return -ENOSPC;
|
||||
|
||||
return muc_uart_pb_write(parent_pdev, buf, count);
|
||||
}
|
||||
|
||||
static ssize_t mmi_char_read(struct file *file,
|
||||
char __user *buf,
|
||||
size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!(file->f_flags & O_NONBLOCK)) {
|
||||
ret = wait_event_interruptible(mmi_char_read_wq,
|
||||
muc_uart_pb_get_q_size(parent_pdev));
|
||||
if(ret < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Count gets updated to bytes read, ret will
|
||||
* be 0 if ok, and negative on an error
|
||||
*/
|
||||
ret = muc_uart_pb_get_q(parent_pdev, buf, &count);
|
||||
if(ret >= 0)
|
||||
ret = count;
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int mmi_char_poll(struct file *file,
|
||||
struct poll_table_struct *pll_table)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
poll_wait(file, &mmi_char_read_wq, pll_table);
|
||||
if(muc_uart_pb_get_q_size(parent_pdev))
|
||||
ret |= POLLIN;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations mmi_char_fops = {
|
||||
.write = mmi_char_write,
|
||||
.read = mmi_char_read,
|
||||
.open = mmi_char_open,
|
||||
.llseek = noop_llseek,
|
||||
.release = mmi_char_release,
|
||||
.poll = mmi_char_poll,
|
||||
};
|
||||
|
||||
int mmi_char_init(int max_write_size, struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
mmi_char_max_write_size = max_write_size;
|
||||
parent_pdev = pdev;
|
||||
|
||||
ret = alloc_chrdev_region(&mmi_char_dev_num, 0, NUM_MINORS, "muc");
|
||||
if (ret < 0)
|
||||
goto err_dev;
|
||||
|
||||
mmi_char_major = MAJOR(mmi_char_dev_num);
|
||||
mmi_char_dev_num = MKDEV(mmi_char_major, 0);
|
||||
mmi_char_class = class_create(THIS_MODULE, "mmi_char_class");
|
||||
|
||||
cdev_init(&mmi_char_cdev, &mmi_char_fops);
|
||||
ret = cdev_add(&mmi_char_cdev, mmi_char_dev_num, 1);
|
||||
if(ret)
|
||||
goto err_cdev;
|
||||
|
||||
if(!device_create(mmi_char_class, NULL,
|
||||
mmi_char_dev_num, NULL, "muc%d", 0))
|
||||
goto err_cdev;
|
||||
|
||||
atomic_set(&mmi_char_open_excl, 0);
|
||||
|
||||
init_waitqueue_head(&mmi_char_read_wq);
|
||||
muc_uart_pb_register_wq(pdev, &mmi_char_read_wq);
|
||||
|
||||
return 0;
|
||||
err_cdev:
|
||||
unregister_chrdev_region(mmi_char_dev_num, NUM_MINORS);
|
||||
err_dev:
|
||||
return 1;
|
||||
}
|
||||
|
||||
void mmi_char_exit(void)
|
||||
{
|
||||
device_destroy(mmi_char_class, mmi_char_dev_num);
|
||||
unregister_chrdev_region(mmi_char_dev_num, NUM_MINORS);
|
||||
class_destroy(mmi_char_class);
|
||||
}
|
||||
19
drivers/mmi_muc_uart/mmi_char.h
Normal file
19
drivers/mmi_muc_uart/mmi_char.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* Copyright (c) 2018, Motorola Mobility LLC. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 _DRIVERS_CHAR_MMI_H_
|
||||
#define _DRIVERS_CHAR_MMI_H_
|
||||
|
||||
int mmi_char_init(int max_write_size, struct platform_device *pdev);
|
||||
void mmi_char_exit(void);
|
||||
|
||||
#endif /* _DRIVERS_CHAR_MMI_H_ */
|
||||
|
|
@ -23,9 +23,11 @@
|
|||
#include <linux/pm.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <soc/qcom/mmi_boot_info.h>
|
||||
#include "muc_uart.h"
|
||||
#include "mmi_uart.h"
|
||||
#include "mmi_tty.h"
|
||||
#include "mmi_char.h"
|
||||
#include "muc_protocol.h"
|
||||
|
||||
#define DRIVERNAME "muc_uart"
|
||||
|
|
@ -60,6 +62,12 @@ struct write_data {
|
|||
uint8_t retries;
|
||||
};
|
||||
|
||||
struct read_data {
|
||||
struct list_head list;
|
||||
size_t payload_length;
|
||||
uint8_t *payload;
|
||||
};
|
||||
|
||||
struct mod_muc_data_t {
|
||||
struct platform_device *pdev;
|
||||
void *uart_data;
|
||||
|
|
@ -80,6 +88,9 @@ struct mod_muc_data_t {
|
|||
struct workqueue_struct *write_wq;
|
||||
struct write_data *write_data_q;
|
||||
int write_q_size;
|
||||
struct read_data *read_data_q;
|
||||
int read_q_size;
|
||||
wait_queue_head_t *read_wq;
|
||||
atomic_t write_credits;
|
||||
struct delayed_work wake_work;
|
||||
struct delayed_work sleep_work;
|
||||
|
|
@ -100,6 +111,7 @@ struct mod_muc_data_t {
|
|||
#define MUC_UART_ACK_TIME 500
|
||||
#define WRITE_CREDIT_INIT 100
|
||||
static DEFINE_SPINLOCK(write_q_lock);
|
||||
static DEFINE_SPINLOCK(read_q_lock);
|
||||
|
||||
/* Share tx between acks and send queue */
|
||||
DEFINE_MUTEX(tx_lock);
|
||||
|
|
@ -378,10 +390,11 @@ static int muc_uart_send(struct mod_muc_data_t *mm_data,
|
|||
}
|
||||
|
||||
/* Any message that needs an ack goes on the queue */
|
||||
static int muc_uart_queue_send(struct mod_muc_data_t *mm_data,
|
||||
static int __muc_uart_queue_send(struct mod_muc_data_t *mm_data,
|
||||
uint16_t cmd,
|
||||
uint8_t *payload,
|
||||
size_t payload_len)
|
||||
size_t payload_len,
|
||||
bool payload_persistant)
|
||||
{
|
||||
struct write_data *write_data;
|
||||
unsigned long flags;
|
||||
|
|
@ -400,19 +413,22 @@ static int muc_uart_queue_send(struct mod_muc_data_t *mm_data,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
write_data->payload = kmalloc(payload_len, GFP_ATOMIC);
|
||||
/* TODO this should probably panic */
|
||||
if (WARN_ON(!write_data->payload)) {
|
||||
atomic_inc(&mm_data->write_credits);
|
||||
kfree(write_data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if(!payload_persistant) {
|
||||
write_data->payload = kmalloc(payload_len, GFP_ATOMIC);
|
||||
/* TODO this should probably panic */
|
||||
if (WARN_ON(!write_data->payload)) {
|
||||
atomic_inc(&mm_data->write_credits);
|
||||
kfree(write_data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(write_data->payload, payload, payload_len);
|
||||
} else
|
||||
write_data->payload = payload;
|
||||
|
||||
INIT_LIST_HEAD(&write_data->list);
|
||||
write_data->payload_length = payload_len;
|
||||
write_data->cmd = cmd;
|
||||
write_data->retries = MUC_UART_RETRY_COUNT;
|
||||
memcpy(write_data->payload, payload, payload_len);
|
||||
|
||||
spin_lock_irqsave(&write_q_lock, flags);
|
||||
if (!mm_data->write_data_q)
|
||||
|
|
@ -435,6 +451,15 @@ static int muc_uart_queue_send(struct mod_muc_data_t *mm_data,
|
|||
return payload_len;
|
||||
}
|
||||
|
||||
static int muc_uart_queue_send(struct mod_muc_data_t *mm_data,
|
||||
uint16_t cmd,
|
||||
uint8_t *payload,
|
||||
size_t payload_len)
|
||||
{
|
||||
return __muc_uart_queue_send(mm_data, cmd, payload,
|
||||
payload_len, false);
|
||||
}
|
||||
|
||||
static void muc_uart_send_work(struct work_struct *w)
|
||||
{
|
||||
struct mod_muc_data_t *mm_data =
|
||||
|
|
@ -730,34 +755,138 @@ static int muc_uart_ps_notifier_cb(struct notifier_block *nb,
|
|||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int muc_uart_tty_rx_cb(struct platform_device *pdev,
|
||||
int idx,
|
||||
uint8_t *payload,
|
||||
size_t payload_length)
|
||||
int muc_uart_pb_register_wq(struct platform_device *pdev,
|
||||
wait_queue_head_t *read_wq)
|
||||
{
|
||||
struct mod_muc_data_t *mm_data =
|
||||
platform_get_drvdata(pdev);
|
||||
|
||||
pr_info("muc_uart_send_packetbus: send from %d msg size %zd.\n",
|
||||
idx,
|
||||
payload_length);
|
||||
/* Only allow the first one who registered */
|
||||
if(mm_data->read_wq)
|
||||
return 1;
|
||||
|
||||
/* TODO idx 1 is for debug */
|
||||
if (idx == MMI_TTY_DEBUG_IDX) {
|
||||
if (!mmi_tty_push_to_us(MMI_TTY_DEFAULT_IDX,
|
||||
payload, payload_length))
|
||||
return payload_length;
|
||||
else
|
||||
return -EAGAIN;
|
||||
} else {
|
||||
mmi_tty_push_to_us(MMI_TTY_DEBUG_IDX,
|
||||
payload, payload_length);
|
||||
time_send_start = ktime_get();
|
||||
return muc_uart_queue_send(mm_data,
|
||||
PACKETBUS_PROT_MSG,
|
||||
payload,
|
||||
payload_length);
|
||||
mm_data->read_wq = read_wq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int muc_uart_pb_get_q(struct platform_device *pdev,
|
||||
char __user *buf,
|
||||
size_t *count)
|
||||
{
|
||||
struct mod_muc_data_t *mm_data =
|
||||
platform_get_drvdata(pdev);
|
||||
|
||||
unsigned long flags;
|
||||
struct read_data *pb_msg = NULL;
|
||||
|
||||
spin_lock_irqsave(&read_q_lock, flags);
|
||||
if (mm_data->read_data_q) {
|
||||
pb_msg = mm_data->read_data_q;
|
||||
if(list_is_last(&pb_msg->list,
|
||||
&mm_data->read_data_q->list))
|
||||
mm_data->read_data_q = NULL;
|
||||
else {
|
||||
mm_data->read_data_q =
|
||||
list_next_entry(pb_msg, list);
|
||||
list_del(&pb_msg->list);
|
||||
}
|
||||
mm_data->read_q_size--;
|
||||
}
|
||||
spin_unlock_irqrestore(&read_q_lock, flags);
|
||||
|
||||
if(!pb_msg)
|
||||
return -ENOMSG;
|
||||
|
||||
if(pb_msg->payload_length > *count)
|
||||
return -ENOSPC;
|
||||
|
||||
if(copy_to_user(buf, pb_msg->payload, pb_msg->payload_length))
|
||||
return -EFAULT;
|
||||
|
||||
*count = pb_msg->payload_length;
|
||||
|
||||
kfree(pb_msg->payload);
|
||||
kfree(pb_msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int muc_uart_pb_get_q_size(struct platform_device *pdev)
|
||||
{
|
||||
struct mod_muc_data_t *mm_data =
|
||||
platform_get_drvdata(pdev);
|
||||
|
||||
return mm_data->read_q_size;
|
||||
}
|
||||
|
||||
static int muc_uart_pb_put_q(struct mod_muc_data_t *mm_data,
|
||||
size_t payload_length,
|
||||
uint8_t *payload)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct read_data *pb_msg;
|
||||
|
||||
if(!payload_length)
|
||||
return 1;
|
||||
|
||||
/* TODO should this list have a size limit?
|
||||
* otherwise a crash of the consumer could
|
||||
* potentially crash the kernel ...
|
||||
*/
|
||||
|
||||
/* Set up the message */
|
||||
pb_msg = kmalloc(sizeof(struct read_data), GFP_KERNEL);
|
||||
if (!pb_msg)
|
||||
return -ENOMEM;
|
||||
|
||||
pb_msg->payload = kmalloc(payload_length, GFP_KERNEL);
|
||||
if (!pb_msg->payload) {
|
||||
kfree(pb_msg);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&pb_msg->list);
|
||||
pb_msg->payload_length = payload_length;
|
||||
memcpy(pb_msg->payload, payload, payload_length);
|
||||
|
||||
spin_lock_irqsave(&read_q_lock, flags);
|
||||
if (!mm_data->read_data_q)
|
||||
mm_data->read_data_q = pb_msg;
|
||||
else
|
||||
list_add_tail(&pb_msg->list,
|
||||
&mm_data->read_data_q->list);
|
||||
mm_data->read_q_size++;
|
||||
spin_unlock_irqrestore(&read_q_lock, flags);
|
||||
|
||||
/* Wake up any process waiting on a message */
|
||||
if(mm_data->read_wq)
|
||||
wake_up(mm_data->read_wq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int muc_uart_pb_write(struct platform_device *pdev,
|
||||
const char __user *buf,
|
||||
size_t count)
|
||||
{
|
||||
uint8_t *payload;
|
||||
struct mod_muc_data_t *mm_data =
|
||||
platform_get_drvdata(pdev);
|
||||
|
||||
pr_info("muc_uart_send_packetbus: send pb msg size %zd.\n",
|
||||
count);
|
||||
|
||||
/* Payload is freed after send work is done */
|
||||
payload = kmalloc(count, GFP_KERNEL);
|
||||
if (!payload)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(payload, buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
time_send_start = ktime_get();
|
||||
return __muc_uart_queue_send(mm_data, PACKETBUS_PROT_MSG,
|
||||
payload,count, true);
|
||||
}
|
||||
|
||||
static void muc_uart_handle_message(struct mod_muc_data_t *mm_data,
|
||||
|
|
@ -788,13 +917,14 @@ static void muc_uart_handle_message(struct mod_muc_data_t *mm_data,
|
|||
break;
|
||||
}
|
||||
case PACKETBUS_PROT_MSG: {
|
||||
if (!mmi_tty_push_to_us(MMI_TTY_DEFAULT_IDX,
|
||||
payload, payload_len)) {
|
||||
if (!muc_uart_pb_put_q(mm_data,
|
||||
payload_len,
|
||||
payload)) {
|
||||
muc_uart_send(mm_data,
|
||||
PACKETBUS_PROT_MSG|MSG_ACK_MASK,
|
||||
NULL, 0);
|
||||
} else {
|
||||
pr_err("muc_uart_handle_message: Couldn't pass packet to us!");
|
||||
pr_err("muc_uart_handle_message: Couldn't add packet to pb q!");
|
||||
muc_uart_send(mm_data,
|
||||
PACKETBUS_PROT_MSG|MSG_NACK_MASK,
|
||||
NULL, 0);
|
||||
|
|
@ -1261,9 +1391,7 @@ static int muc_uart_probe(struct platform_device *pdev)
|
|||
pdev))
|
||||
goto err2;
|
||||
|
||||
if (mmi_tty_init(muc_uart_tty_rx_cb,
|
||||
(UART_MAX_MSG_SIZE - MSG_META_DATA_SIZE),
|
||||
pdev))
|
||||
if (mmi_char_init(UART_MAX_MSG_SIZE - MSG_META_DATA_SIZE, pdev))
|
||||
goto err3;
|
||||
|
||||
mm_data->phone_psy = devm_power_supply_register(&pdev->dev,
|
||||
|
|
@ -1328,7 +1456,7 @@ static int muc_uart_remove(struct platform_device *pdev)
|
|||
cancel_work_sync(&mm_data->ps_notify_work);
|
||||
destroy_workqueue(mm_data->write_wq);
|
||||
mmi_uart_exit(mm_data->uart_data);
|
||||
mmi_tty_exit();
|
||||
mmi_char_exit();
|
||||
sysfs_remove_groups(&pdev->dev.kobj, muc_uart_groups);
|
||||
kobject_uevent(&pdev->dev.kobj, KOBJ_REMOVE);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
|
|
|||
26
drivers/mmi_muc_uart/muc_uart.h
Normal file
26
drivers/mmi_muc_uart/muc_uart.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* Copyright (c) 2018, Motorola Mobility LLC. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 _DRIVERS_MUC_UART_H_
|
||||
#define _DRIVERS_MUC_UART_H_
|
||||
|
||||
int muc_uart_pb_get_q_size(struct platform_device *pdev);
|
||||
int muc_uart_pb_get_q(struct platform_device *pdev,
|
||||
char __user *buf,
|
||||
size_t *count);
|
||||
int muc_uart_pb_register_wq(struct platform_device *pdev,
|
||||
wait_queue_head_t *read_wq);
|
||||
int muc_uart_pb_write(struct platform_device *pdev,
|
||||
const char __user *buf,
|
||||
size_t count);
|
||||
|
||||
#endif /* _DRIVERS_MUC_UART_H_ */
|
||||
Loading…
Reference in a new issue