blob: 64e27896163c329493911b21cb5c30699239caa0 [file] [log] [blame]
/* Copyright (c) 2013-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/completion.h>
#include <linux/debugfs.h>
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/if_ether.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/msm_ipa.h>
#include <linux/mutex.h>
#include <linux/skbuff.h>
#include <linux/types.h>
#include <linux/ipa.h>
#include "ipa_i.h"
#define TETH_BRIDGE_DRV_NAME "ipa_tethering_bridge"
#define TETH_DBG(fmt, args...) \
pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d " fmt, \
__func__, __LINE__, ## args)
#define TETH_DBG_FUNC_ENTRY() \
pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d ENTRY\n", __func__, __LINE__)
#define TETH_DBG_FUNC_EXIT() \
pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d EXIT\n", __func__, __LINE__)
#define TETH_ERR(fmt, args...) \
pr_err(TETH_BRIDGE_DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
/**
* enum teth_init_status - bridge initialization state
* (NOT_INITIALIZED / INITIALIZED/ ERROR)
*/
enum teth_init_status {
TETH_NOT_INITIALIZED,
TETH_INITIALIZED,
TETH_INITIALIZATION_ERROR,
};
/**
* struct teth_bridge_ctx - Tethering bridge driver context information
* @class: kernel class pointer
* @dev_num: kernel device number
* @dev: kernel device struct pointer
* @cdev: kernel character device struct
* finished its resource release procedure
* @ch_init_cnt: count the initialized channels
* @init_status: bridge initialization state
* @init_mutex: for the initialization, connect and disconnect synchronization
*/
struct teth_bridge_ctx {
struct class *class;
dev_t dev_num;
struct device *dev;
struct cdev cdev;
u16 ch_init_cnt;
enum teth_init_status init_status;
struct mutex init_mutex;
};
static struct teth_bridge_ctx *teth_ctx;
/**
* teth_bridge_init() - Initialize the Tethering bridge driver
* @params - in/out params for USB initialization API (please look at struct
* definition for more info)
*
* USB driver gets a pointer to a callback function (usb_notify_cb) and an
* associated data. USB driver installs this callback function in the call to
* ipa_connect().
*
* Builds IPA resource manager dependency graph.
*
* Return codes: 0: success,
* -EINVAL - Bad parameter
* Other negative value - Failure
*/
int teth_bridge_init(struct teth_bridge_init_params *params)
{
int res = 0;
TETH_DBG_FUNC_ENTRY();
if (!params) {
TETH_ERR("Bad parameter\n");
TETH_DBG_FUNC_EXIT();
return -EINVAL;
}
params->usb_notify_cb = NULL;
params->private_data = NULL;
params->skip_ep_cfg = true;
mutex_lock(&teth_ctx->init_mutex);
if (teth_ctx->init_status == TETH_INITIALIZATION_ERROR) {
res = -EPERM;
goto bail;
}
if (teth_ctx->init_status == TETH_INITIALIZED) {
teth_ctx->ch_init_cnt++;
res = 0;
goto bail;
}
teth_ctx->init_status = TETH_INITIALIZED;
teth_ctx->ch_init_cnt++;
res = 0;
goto bail;
bail:
mutex_unlock(&teth_ctx->init_mutex);
TETH_DBG_FUNC_EXIT();
return res;
}
EXPORT_SYMBOL(teth_bridge_init);
/**
* teth_bridge_disconnect() - Disconnect tethering bridge module
*/
int teth_bridge_disconnect(enum ipa_client_type client)
{
return 0;
}
EXPORT_SYMBOL(teth_bridge_disconnect);
/**
* teth_bridge_connect() - Connect bridge for a tethered Rmnet / MBIM call
* @connect_params: Connection info
*
* Return codes: 0: success
* -EINVAL: invalid parameters
* -EPERM: Operation not permitted as the bridge is already
* connected
*/
int teth_bridge_connect(struct teth_bridge_connect_params *connect_params)
{
return 0;
}
EXPORT_SYMBOL(teth_bridge_connect);
static long teth_bridge_ioctl(struct file *filp,
unsigned int cmd,
unsigned long arg)
{
IPAERR("No ioctls are supported for krypton !\n");
return -ENOIOCTLCMD;
}
static const struct file_operations teth_bridge_drv_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = teth_bridge_ioctl,
};
/**
* teth_bridge_driver_init() - Initialize tethering bridge driver
*
*/
int teth_bridge_driver_init(void)
{
int res;
TETH_DBG("Tethering bridge driver init\n");
teth_ctx = kzalloc(sizeof(*teth_ctx), GFP_KERNEL);
if (!teth_ctx) {
TETH_ERR("kzalloc err.\n");
return -ENOMEM;
}
teth_ctx->class = class_create(THIS_MODULE, TETH_BRIDGE_DRV_NAME);
res = alloc_chrdev_region(&teth_ctx->dev_num, 0, 1,
TETH_BRIDGE_DRV_NAME);
if (res) {
TETH_ERR("alloc_chrdev_region err.\n");
res = -ENODEV;
goto fail_alloc_chrdev_region;
}
teth_ctx->dev = device_create(teth_ctx->class, NULL, teth_ctx->dev_num,
teth_ctx, TETH_BRIDGE_DRV_NAME);
if (IS_ERR(teth_ctx->dev)) {
TETH_ERR(":device_create err.\n");
res = -ENODEV;
goto fail_device_create;
}
cdev_init(&teth_ctx->cdev, &teth_bridge_drv_fops);
teth_ctx->cdev.owner = THIS_MODULE;
teth_ctx->cdev.ops = &teth_bridge_drv_fops;
res = cdev_add(&teth_ctx->cdev, teth_ctx->dev_num, 1);
if (res) {
TETH_ERR(":cdev_add err=%d\n", -res);
res = -ENODEV;
goto fail_cdev_add;
}
teth_ctx->ch_init_cnt = 0;
teth_ctx->init_status = TETH_NOT_INITIALIZED;
mutex_init(&teth_ctx->init_mutex);
TETH_DBG("Tethering bridge driver init OK\n");
return 0;
fail_cdev_add:
device_destroy(teth_ctx->class, teth_ctx->dev_num);
fail_device_create:
unregister_chrdev_region(teth_ctx->dev_num, 1);
fail_alloc_chrdev_region:
kfree(teth_ctx);
teth_ctx = NULL;
return res;
}
EXPORT_SYMBOL(teth_bridge_driver_init);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Tethering bridge driver");