blob: 08c74ee3a04c1982423b3c65152e36af200a6c69 [file] [log] [blame]
/* Copyright (c) 2014, The Linux Foundation. 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/slab.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/diagchar.h>
#include <linux/sched.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/ratelimit.h>
#include "diagchar.h"
#include "diag_mux.h"
#include "diag_usb.h"
#include "diag_memorydevice.h"
struct diag_logger_t *logger;
static struct diag_logger_t usb_logger;
static struct diag_logger_t md_logger;
static struct diag_logger_ops usb_log_ops = {
.open = diag_usb_connect_all,
.close = diag_usb_disconnect_all,
.queue_read = diag_usb_queue_read,
.write = diag_usb_write,
};
static struct diag_logger_ops md_log_ops = {
.open = diag_md_open_all,
.close = diag_md_close_all,
.queue_read = NULL,
.write = diag_md_write,
};
int diag_mux_init()
{
logger = kzalloc(NUM_MUX_PROC * sizeof(struct diag_logger_t),
GFP_KERNEL);
if (!logger)
return -ENOMEM;
kmemleak_not_leak(logger);
usb_logger.mode = DIAG_USB_MODE;
usb_logger.log_ops = &usb_log_ops;
md_logger.mode = DIAG_MEMORY_DEVICE_MODE;
md_logger.log_ops = &md_log_ops;
diag_md_init();
/*
* Set USB logging as the default logger. This is the mode
* Diag should be in when it initializes.
*/
logger = &usb_logger;
return 0;
}
void diag_mux_exit()
{
kfree(logger);
}
int diag_mux_register(int proc, int ctx, struct diag_mux_ops *ops)
{
int err = 0;
if (!ops)
return -EINVAL;
if (proc < 0 || proc >= NUM_MUX_PROC)
return 0;
/* Register with USB logger */
usb_logger.ops[proc] = ops;
err = diag_usb_register(proc, ctx, ops);
if (err) {
pr_err("diag: MUX: unable to register usb operations for proc: %d, err: %d\n",
proc, err);
return err;
}
md_logger.ops[proc] = ops;
err = diag_md_register(proc, ctx, ops);
if (err) {
pr_err("diag: MUX: unable to register md operations for proc: %d, err: %d\n",
proc, err);
return err;
}
return 0;
}
int diag_mux_queue_read(int proc)
{
if (proc < 0 || proc >= NUM_MUX_PROC)
return -EINVAL;
if (!logger)
return -EIO;
if (logger->log_ops && logger->log_ops->queue_read)
return logger->log_ops->queue_read(proc);
return 0;
}
int diag_mux_write(int proc, unsigned char *buf, int len, int ctx)
{
if (proc < 0 || proc >= NUM_MUX_PROC)
return -EINVAL;
if (logger && logger->log_ops && logger->log_ops->write)
return logger->log_ops->write(proc, buf, len, ctx);
return 0;
}
int diag_mux_switch_logging(int new_mode)
{
struct diag_logger_t *new_logger = NULL;
switch (new_mode) {
case DIAG_USB_MODE:
new_logger = &usb_logger;
break;
case DIAG_MEMORY_DEVICE_MODE:
new_logger = &md_logger;
break;
default:
pr_err("diag: Invalid mode %d in %s\n", new_mode, __func__);
return -EINVAL;
}
if (logger) {
logger->log_ops->close();
logger = new_logger;
logger->log_ops->open();
}
return 0;
}