blob: 542cc2c29ac5b714fda225246634f9839c26b7e1 [file] [log] [blame]
/** @file bt_proc.c
*
* @brief This file handle the functions for proc files
*
* Copyright (C) 2007-2015, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available along with the File in the gpl.txt file or by writing to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
#include <linux/proc_fs.h>
#include "bt_drv.h"
#include "bt_sdio.h"
/** proc diretory root */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
#define PROC_DIR NULL
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
#define PROC_DIR (&proc_root)
#else
#define PROC_DIR proc_net
#endif
static const char kCmd52Header[] = "sdcmd52rw=";
static const size_t kCmd52HeaderLen = sizeof(kCmd52Header) - 1;
/** Proc mbt directory entry */
static struct proc_dir_entry *proc_mbt;
#define CMD52_STR_LEN 50
static bt_private *bpriv;
static char cmd52_string[CMD52_STR_LEN];
struct proc_data {
/** Read length */
int rdlen;
/** Read buffer */
char *rdbuf;
/** Write length */
int wrlen;
/** Maximum write length */
int maxwrlen;
/** Write buffer */
char *wrbuf;
void (*on_close) (struct inode *, struct file *);
};
/** Default file permission */
#define DEFAULT_FILE_PERM 0644
/** Bluetooth device offset */
#define OFFSET_BT_DEV 0x01
/** Bluetooth adapter offset */
#define OFFSET_BT_ADAPTER 0x02
/** Show integer */
#define SHOW_INT 0x10
/** Show hex */
#define SHOW_HEX 0x20
/** Show string */
#define SHOW_STRING 0x40
/** Device size */
#define item_dev_size(n) (sizeof((bt_dev_t *)0)->n)
/** Device address */
#define item_dev_addr(n) ((t_ptr) &((bt_dev_t *)0)->n)
/** Adapter size */
#define item_adapter_size(n) (sizeof((bt_adapter *)0)->n)
/** Adapter address */
#define item_adapter_addr(n) ((t_ptr) &((bt_adapter *)0)->n)
static struct item_data config_items[] = {
#ifdef DEBUG_LEVEL1
{"drvdbg", sizeof(u32), (t_ptr)&mbt_drvdbg, 0, SHOW_HEX}
,
#endif
{"idle_timeout", item_dev_size(idle_timeout), 0,
item_dev_addr(idle_timeout), OFFSET_BT_DEV | SHOW_HEX}
,
{"psmode", item_dev_size(psmode), 0, item_dev_addr(psmode),
OFFSET_BT_DEV | SHOW_INT}
,
{"pscmd", item_dev_size(pscmd), 0, item_dev_addr(pscmd),
OFFSET_BT_DEV | SHOW_INT}
,
{"hsmode", item_dev_size(hsmode), 0, item_dev_addr(hsmode),
OFFSET_BT_DEV | SHOW_INT}
,
{"hscmd", item_dev_size(hscmd), 0, item_dev_addr(hscmd),
OFFSET_BT_DEV | SHOW_INT}
,
{"gpio_gap", item_dev_size(gpio_gap), 0, item_dev_addr(gpio_gap),
OFFSET_BT_DEV | SHOW_HEX}
,
{"hscfgcmd", item_dev_size(hscfgcmd), 0, item_dev_addr(hscfgcmd),
OFFSET_BT_DEV | SHOW_INT}
,
{"sdio_pull_cfg", item_dev_size(sdio_pull_cfg), 0,
item_dev_addr(sdio_pull_cfg), OFFSET_BT_DEV | SHOW_HEX}
,
{"sdio_pull_ctrl", item_dev_size(sdio_pull_ctrl), 0,
item_dev_addr(sdio_pull_ctrl), OFFSET_BT_DEV | SHOW_INT}
,
{"test_mode", item_dev_size(test_mode), 0, item_dev_addr(test_mode),
OFFSET_BT_DEV | SHOW_INT}
,
};
static struct item_data status_items[] = {
{"version", item_adapter_size(drv_ver), 0, item_adapter_addr(drv_ver),
OFFSET_BT_ADAPTER | SHOW_STRING},
{"tx_dnld_rdy", item_dev_size(tx_dnld_rdy), 0,
item_dev_addr(tx_dnld_rdy),
OFFSET_BT_DEV | SHOW_INT},
{"psmode", item_adapter_size(psmode), 0, item_adapter_addr(psmode),
OFFSET_BT_ADAPTER | SHOW_INT},
{"hs_state", item_adapter_size(hs_state), 0,
item_adapter_addr(hs_state),
OFFSET_BT_ADAPTER | SHOW_INT},
{"hs_skip", item_adapter_size(hs_skip), 0, item_adapter_addr(hs_skip),
OFFSET_BT_ADAPTER | SHOW_INT},
{"ps_state", item_adapter_size(ps_state), 0,
item_adapter_addr(ps_state),
OFFSET_BT_ADAPTER | SHOW_INT},
{"WakeupTries", item_adapter_size(WakeupTries), 0,
item_adapter_addr(WakeupTries), OFFSET_BT_ADAPTER | SHOW_INT},
{"irq_recv", item_adapter_size(irq_recv), 0,
item_adapter_addr(irq_recv),
OFFSET_BT_ADAPTER | SHOW_INT},
{"irq_done", item_adapter_size(irq_done), 0,
item_adapter_addr(irq_done),
OFFSET_BT_ADAPTER | SHOW_INT},
{"skb_pending", item_adapter_size(skb_pending), 0,
item_adapter_addr(skb_pending), OFFSET_BT_ADAPTER | SHOW_INT},
};
static struct item_data debug_items[] = {
{"sdcmd52rw", 0, (t_ptr)cmd52_string, 0, SHOW_STRING},
};
/**
* @brief convert string to number
*
* @param s pointer to numbered string
* @return converted number from string s
*/
int
string_to_number(char *s)
{
int r = 0;
int base = 0;
int pn = 1;
if (strncmp(s, "-", 1) == 0) {
pn = -1;
s++;
}
if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) {
base = 16;
s += 2;
} else
base = 10;
for (s = s; *s != 0; s++) {
if ((*s >= '0') && (*s <= '9'))
r = (r * base) + (*s - '0');
else if ((*s >= 'A') && (*s <= 'F'))
r = (r * base) + (*s - 'A' + 10);
else if ((*s >= 'a') && (*s <= 'f'))
r = (r * base) + (*s - 'a' + 10);
else
break;
}
return r * pn;
}
/**
* @brief Create cmd52 string
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS
*/
static int
form_cmd52_string(bt_private *priv)
{
ENTER();
memset(cmd52_string, 0, CMD52_STR_LEN);
snprintf(cmd52_string, CMD52_STR_LEN - 1, "BT: %d 0x%0x 0x%02X",
priv->bt_dev.cmd52_func, priv->bt_dev.cmd52_reg,
priv->bt_dev.cmd52_val);
LEAVE();
return BT_STATUS_SUCCESS;
}
/*
* @brief Parse cmd52 string
*
* @param buffer A pointer user buffer
* @param len Length of user buffer
* @param func Parsed func number
* @param reg Parsed reg value
* @param val Parsed value to set
* @return BT_STATUS_SUCCESS
*/
static int
parse_cmd52_string(const char __user * buffer, size_t len,
int *func, int *reg, int *val)
{
char cmd[CMD52_STR_LEN] = {0};
char *string = NULL;
char *pos = NULL;
ENTER();
BUG_ON(len < kCmd52HeaderLen ||
strncmp(buffer, kCmd52Header, kCmd52HeaderLen) != 0);
size_t size = min(len - kCmd52HeaderLen, sizeof(cmd));
strlcpy(cmd, buffer + kCmd52HeaderLen, size);
*func = -1;
*reg = -1;
*val = -1;
string = strstrip(cmd);
/* Get func */
pos = strsep(&string, " \t");
if (pos)
*func = string_to_number(pos);
/* Get reg */
pos = strsep(&string, " \t");
if (pos)
*reg = string_to_number(pos);
/* Get val (optional) */
pos = strsep(&string, " \t");
if (pos)
*val = string_to_number(pos);
LEAVE();
return BT_STATUS_SUCCESS;
}
/**
* @brief This function handle generic proc file close
*
* @param inode A pointer to inode structure
* @param file A pointer to file structure
* @return BT_STATUS_SUCCESS
*/
static int
proc_close(struct inode *inode, struct file *file)
{
struct proc_data *pdata = file->private_data;
ENTER();
if (pdata) {
if (pdata->on_close != NULL)
pdata->on_close(inode, file);
kfree(pdata->rdbuf);
kfree(pdata->wrbuf);
kfree(pdata);
}
LEAVE();
return BT_STATUS_SUCCESS;
}
/**
* @brief This function handle generic proc file read
*
* @param file A pointer to file structure
* @param buffer A pointer to output buffer
* @param len number of byte to read
* @param offset A pointer to offset of file
* @return number of output data
*/
static ssize_t
proc_read(struct file *file, char __user * buffer, size_t len, loff_t * offset)
{
loff_t pos = *offset;
struct proc_data *pdata = (struct proc_data *)file->private_data;
if ((!pdata->rdbuf) || (pos < 0))
return -EINVAL;
if (pos >= pdata->rdlen)
return 0;
if (len > pdata->rdlen - pos)
len = pdata->rdlen - pos;
if (copy_to_user(buffer, pdata->rdbuf + pos, len))
return -EFAULT;
*offset = pos + len;
return len;
}
/**
* @brief This function handle generic proc file write
*
* @param file A pointer to file structure
* @param buffer A pointer to input buffer
* @param len number of byte to write
* @param offset A pointer to offset of file
* @return number of input data
*/
static ssize_t
proc_write(struct file *file,
const char __user * buffer, size_t len, loff_t * offset)
{
loff_t pos = *offset;
struct proc_data *pdata = (struct proc_data *)file->private_data;
int func = 0, reg = 0, val = 0;
if (!pdata->wrbuf || (pos < 0))
return -EINVAL;
if (pos >= pdata->maxwrlen)
return 0;
if (len > pdata->maxwrlen - pos)
len = pdata->maxwrlen - pos;
if (copy_from_user(pdata->wrbuf + pos, buffer, len))
return -EFAULT;
if (len >= kCmd52HeaderLen &&
!strncmp(buffer, kCmd52Header, kCmd52HeaderLen)) {
parse_cmd52_string(buffer, len, &func, &reg, &val);
sd_write_cmd52_val(bpriv, func, reg, val);
}
if (!strncmp(buffer, "debug_dump", strlen("debug_dump"))) {
bt_dump_sdio_regs(bpriv);
bt_dump_firmware_info_v2(bpriv);
}
if (!strncmp(buffer, "fw_reload", strlen("fw_reload"))) {
PRINTM(MSG, "Request fw_reload...\n");
bt_request_fw_reload(bpriv);
}
if (pos + len > pdata->wrlen)
pdata->wrlen = len + file->f_pos;
*offset = pos + len;
return len;
}
/**
* @brief This function handle the generic file close
*
* @param inode A pointer to inode structure
* @param file A pointer to file structure
* @return N/A
*/
static void
proc_on_close(struct inode *inode, struct file *file)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
struct proc_private_data *priv = PDE_DATA(inode);
#else
struct proc_private_data *priv = PDE(inode)->data;
#endif
struct proc_data *pdata = file->private_data;
char *line;
int i;
ENTER();
if (!pdata->wrlen)
return;
line = pdata->wrbuf;
while (line[0]) {
for (i = 0; i < priv->num_items; i++) {
if (!strncmp
(line, priv->pdata[i].name,
strlen(priv->pdata[i].name))) {
line += strlen(priv->pdata[i].name) + 1;
if (priv->pdata[i].size == 1)
*((u8 *)priv->pdata[i].addr) =
(u8)string_to_number(line);
else if (priv->pdata[i].size == 2)
*((u16 *) priv->pdata[i].addr) =
(u16) string_to_number(line);
else if (priv->pdata[i].size == 4)
*((u32 *)priv->pdata[i].addr) =
(u32)string_to_number(line);
}
}
while (line[0] && line[0] != '\n')
line++;
if (line[0])
line++;
}
if (priv->pbt->bt_dev.hscmd || priv->pbt->bt_dev.pscmd
|| priv->pbt->bt_dev.sdio_pull_ctrl
|| priv->pbt->bt_dev.test_mode || priv->pbt->bt_dev.hscfgcmd) {
bt_prepare_command(priv->pbt);
wake_up_interruptible(&priv->pbt->MainThread.waitQ);
}
LEAVE();
return;
}
/**
* @brief This function handle the generic file open
*
* @param inode A pointer to inode structure
* @param file A pointer to file structure
* @return BT_STATUS_SUCCESS or other error no.
*/
static int
proc_open(struct inode *inode, struct file *file)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
struct proc_private_data *priv = PDE_DATA(inode);
#else
struct proc_private_data *priv = PDE(inode)->data;
#endif
struct proc_data *pdata;
int i;
char *p;
u32 val = 0;
ENTER();
priv->pbt->adapter->skb_pending =
skb_queue_len(&priv->pbt->adapter->tx_queue);
file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL);
if (file->private_data == NULL) {
PRINTM(ERROR, "BT: Can not alloc mem for proc_data\n");
LEAVE();
return -ENOMEM;
}
pdata = (struct proc_data *)file->private_data;
pdata->rdbuf = kmalloc(priv->bufsize, GFP_KERNEL);
if (pdata->rdbuf == NULL) {
PRINTM(ERROR, "BT: Can not alloc mem for rdbuf\n");
kfree(file->private_data);
LEAVE();
return -ENOMEM;
}
if (priv->fileflag == DEFAULT_FILE_PERM) {
pdata->wrbuf = kzalloc(priv->bufsize, GFP_KERNEL);
if (pdata->wrbuf == NULL) {
PRINTM(ERROR, "BT: Can not alloc mem for wrbuf\n");
kfree(pdata->rdbuf);
kfree(file->private_data);
return -ENOMEM;
}
pdata->maxwrlen = priv->bufsize;
pdata->on_close = proc_on_close;
}
p = pdata->rdbuf;
for (i = 0; i < priv->num_items; i++) {
if (priv->pdata[i].size == 1)
val = *((u8 *)priv->pdata[i].addr);
else if (priv->pdata[i].size == 2)
val = *((u16 *) priv->pdata[i].addr);
else if (priv->pdata[i].size == 4)
val = *((u32 *)priv->pdata[i].addr);
if (priv->pdata[i].flag & SHOW_INT)
p += sprintf(p, "%s=%d\n", priv->pdata[i].name, val);
else if (priv->pdata[i].flag & SHOW_HEX)
p += sprintf(p, "%s=0x%x\n", priv->pdata[i].name, val);
else if (priv->pdata[i].flag & SHOW_STRING) {
if (!strncmp
(priv->pdata[i].name, "sdcmd52rw",
strlen("sdcmd52rw"))) {
sd_read_cmd52_val(bpriv);
form_cmd52_string(bpriv);
}
p += sprintf(p, "%s=%s\n", priv->pdata[i].name,
(char *)priv->pdata[i].addr);
}
}
pdata->rdlen = strlen(pdata->rdbuf);
LEAVE();
return BT_STATUS_SUCCESS;
}
static const struct file_operations proc_read_ops = {
.read = proc_read,
.open = proc_open,
.release = proc_close
};
static const struct file_operations proc_rw_ops = {
.read = proc_read,
.write = proc_write,
.open = proc_open,
.release = proc_close
};
static struct proc_private_data proc_files[] = {
{"status", S_IRUGO, 1024,
sizeof(status_items) / sizeof(status_items[0]),
&status_items[0], NULL, &proc_read_ops}
,
{"config", DEFAULT_FILE_PERM, 512,
sizeof(config_items) / sizeof(config_items[0]), &config_items[0], NULL,
&proc_rw_ops}
,
{"debug", DEFAULT_FILE_PERM, 512,
sizeof(debug_items) / sizeof(debug_items[0]), &debug_items[0], NULL,
&proc_rw_ops}
,
};
/**
* @brief Proc read function for histogram
*
* @param sfp pointer to seq_file structure
* @param data
*
* @return Number of output data or MLAN_STATUS_FAILURE
*/
static int
bt_histogram_read(struct seq_file *sfp, void *data)
{
bt_hist_proc_data *pdata = (bt_hist_proc_data *) sfp->private;
bt_private *priv = (bt_private *)pdata->pbt;
u8 ant_num;
int i, j;
ENTER();
if (!priv) {
LEAVE();
return -EFAULT;
}
bt_get_histogram(priv);
ant_num = priv->hist_data_len / sizeof(bt_histogram_data);
seq_printf(sfp, "BT histogram:\n");
seq_printf(sfp, "antenna: 0=2.4G antenna a, 1=2.4G antenna b\n\n");
if (ant_num < 1) {
seq_printf(sfp, "no histogram data from FW\n");
LEAVE();
return 0;
}
for (i = 0; i < ant_num; i++) {
if (pdata->antenna != priv->hist_data[i].antenna)
continue;
seq_printf(sfp, "antenna %d\n", priv->hist_data[i].antenna);
switch (priv->hist_data[i].powerclass) {
case 2:
seq_printf(sfp, "Power class=1.5\n");
break;
case 5:
seq_printf(sfp, "Power class=2\n");
break;
case 6:
seq_printf(sfp, "Power class=1\n");
break;
default:
seq_printf(sfp, "Power class=%d\n",
priv->hist_data[i].powerclass);
break;
}
for (j = 0; j < (MAX_BT_LINK + MAX_BLE_LINK); j++) {
switch (priv->hist_data[i].link[j].txrxrate) {
case BDR_RATE_1M:
seq_printf(sfp,
"BT link[%d]: TxPower=%d dBm, TxRx Rate=BDR(1 mbps), RSSI=%d dBm\n",
j + 1,
priv->hist_data[i].link[j].txpower,
priv->hist_data[i].link[j].rssi);
break;
case EDR_RATE_2_3M:
seq_printf(sfp,
"BT link[%d]: TxPower=%d dBm, TxRx Rate=EDR(2/3 mbps), RSSI=%d dBm\n",
j + 1,
priv->hist_data[i].link[j].txpower,
priv->hist_data[i].link[j].rssi);
break;
case BLE_RATE_1M:
seq_printf(sfp,
"BLE link[%d]: TxPower=%d dBm, TxRx Rate=BLE(1 mbps), RSSI=%d dBm\n",
j - MAX_BT_LINK + 0x80,
priv->hist_data[i].link[j].txpower,
priv->hist_data[i].link[j].rssi);
break;
default:
if (j < MAX_BT_LINK)
seq_printf(sfp,
"BT link[%d]: TxPower=%d dBm, TxRx Rate=(%d), RSSI=%d dBm\n",
j + 1,
priv->hist_data[i].link[j].
txpower,
priv->hist_data[i].link[j].
txrxrate,
priv->hist_data[i].link[j].
rssi);
else
seq_printf(sfp,
"BLE link[%d]: TxPower=%d dBm, TxRx Rate=(%d), RSSI=%d dBm\n",
j - MAX_BT_LINK + 0x80,
priv->hist_data[i].link[j].
txpower,
priv->hist_data[i].link[j].
txrxrate,
priv->hist_data[i].link[j].
rssi);
break;
}
}
seq_printf(sfp, "\n");
}
LEAVE();
return 0;
}
static int
bt_histogram_proc_open(struct inode *inode, struct file *file)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
return single_open(file, bt_histogram_read, PDE_DATA(inode));
#else
return single_open(file, bt_histogram_read, PDE(inode)->data);
#endif
}
static const struct file_operations histogram_proc_fops = {
.owner = THIS_MODULE,
.open = bt_histogram_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* @brief This function initializes proc entry
*
* @param priv A pointer to bt_private structure
* @param m_dev A pointer to struct m_dev
* @param seq Sequence number
*
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
int
bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq)
{
int ret = BT_STATUS_SUCCESS;
struct proc_dir_entry *entry;
int i, j;
char hist_entry[50];
ENTER();
bpriv = priv;
memset(cmd52_string, 0, CMD52_STR_LEN);
if (proc_mbt) {
priv->dev_proc[seq].proc_entry =
proc_mkdir(m_dev->name, proc_mbt);
if (!priv->dev_proc[seq].proc_entry) {
PRINTM(ERROR, "BT: Could not mkdir %s!\n", m_dev->name);
ret = BT_STATUS_FAILURE;
goto done;
}
priv->dev_proc[seq].hist_entry =
proc_mkdir("histogram", priv->dev_proc[seq].proc_entry);
if (!priv->dev_proc[seq].hist_entry) {
PRINTM(ERROR, "BT: Could not mkdir histogram!\n");
ret = BT_STATUS_FAILURE;
goto done;
}
for (i = 0; i < MAX_ANTENNA_NUM; i++) {
priv->hist_proc[i].antenna = i;
priv->hist_proc[i].pbt = priv;
snprintf(hist_entry, sizeof(hist_entry), "bt-ant%d", i);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
entry = proc_create_data(hist_entry, 0644,
priv->dev_proc[seq].hist_entry,
&histogram_proc_fops,
&priv->hist_proc[i]);
if (entry == NULL)
#else
entry = create_proc_entry(hist_entry, 0644,
priv->dev_proc[seq].
hist_entry);
if (entry) {
entry->data = &priv->hist_proc[i];
entry->proc_fops = &histogram_proc_fops;
} else
#endif
{
PRINTM(MSG,
"Fail to create histogram proc %s\n",
hist_entry);
ret = BT_STATUS_FAILURE;
goto done;
}
}
priv->dev_proc[seq].pfiles =
kmalloc(sizeof(proc_files), GFP_ATOMIC);
if (!priv->dev_proc[seq].pfiles) {
PRINTM(ERROR,
"BT: Could not alloc memory for pfile!\n");
ret = BT_STATUS_FAILURE;
goto done;
}
memcpy((u8 *)priv->dev_proc[seq].pfiles, (u8 *)proc_files,
sizeof(proc_files));
priv->dev_proc[seq].num_proc_files = ARRAY_SIZE(proc_files);
for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++)
priv->dev_proc[seq].pfiles[j].pdata = NULL;
for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
priv->dev_proc[seq].pfiles[j].pdata =
kmalloc(priv->dev_proc[seq].pfiles[j].
num_items * sizeof(struct item_data),
GFP_ATOMIC);
if (!priv->dev_proc[seq].pfiles[j].pdata) {
PRINTM(ERROR,
"BT: Could not alloc memory for pdata!\n");
ret = BT_STATUS_FAILURE;
goto done;
}
memcpy((u8 *)priv->dev_proc[seq].pfiles[j].pdata,
(u8 *)proc_files[j].pdata,
priv->dev_proc[seq].pfiles[j].num_items *
sizeof(struct item_data));
for (i = 0; i < priv->dev_proc[seq].pfiles[j].num_items;
i++) {
if (priv->dev_proc[seq].pfiles[j].
pdata[i].flag & OFFSET_BT_DEV)
priv->dev_proc[seq].pfiles[j].pdata[i].
addr =
priv->dev_proc[seq].pfiles[j].
pdata[i].offset +
(t_ptr)&priv->bt_dev;
if (priv->dev_proc[seq].pfiles[j].
pdata[i].flag & OFFSET_BT_ADAPTER)
priv->dev_proc[seq].pfiles[j].pdata[i].
addr =
priv->dev_proc[seq].pfiles[j].
pdata[i].offset +
(t_ptr)priv->adapter;
}
priv->dev_proc[seq].pfiles[j].pbt = priv;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
entry = proc_create_data(proc_files[j].name,
S_IFREG | proc_files[j].
fileflag,
priv->dev_proc[seq].proc_entry,
proc_files[j].fops,
&priv->dev_proc[seq].
pfiles[j]);
if (entry == NULL)
#else
entry = create_proc_entry(proc_files[j].name,
S_IFREG | proc_files[j].
fileflag,
priv->dev_proc[seq].
proc_entry);
if (entry) {
entry->data = &priv->dev_proc[seq].pfiles[j];
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
entry->owner = THIS_MODULE;
#endif
entry->proc_fops = proc_files[j].fops;
} else
#endif
PRINTM(MSG, "BT: Fail to create proc %s\n",
proc_files[j].name);
}
}
done:
if (ret == BT_STATUS_FAILURE) {
if (priv->dev_proc[seq].proc_entry) {
remove_proc_entry(m_dev->name, proc_mbt);
priv->dev_proc[seq].proc_entry = NULL;
}
if (priv->dev_proc[seq].pfiles) {
for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
if (priv->dev_proc[seq].pfiles[j].pdata) {
kfree(priv->dev_proc[seq].pfiles[j].
pdata);
priv->dev_proc[seq].pfiles[j].pdata =
NULL;
}
}
kfree(priv->dev_proc[seq].pfiles);
priv->dev_proc[seq].pfiles = NULL;
}
}
LEAVE();
return ret;
}
/**
* @brief This function removes proc interface
*
* @param priv A pointer to bt_private structure
* @return N/A
*/
void
bt_proc_remove(bt_private *priv)
{
int j, i;
char hist_entry[50];
ENTER();
PRINTM(INFO, "BT: Remove Proc Interface\n");
if (proc_mbt) {
for (i = 0; i < MAX_RADIO_FUNC; i++) {
if (!priv->dev_proc[i].proc_entry)
continue;
for (j = 0; j < ARRAY_SIZE(proc_files); j++) {
remove_proc_entry(proc_files[j].name,
priv->dev_proc[i].proc_entry);
}
for (j = 0; j < MAX_ANTENNA_NUM; j++) {
snprintf(hist_entry, sizeof(hist_entry),
"bt-ant%d", j);
remove_proc_entry(hist_entry,
priv->dev_proc[i].hist_entry);
}
remove_proc_entry("histogram",
priv->dev_proc[i].proc_entry);
remove_proc_entry(priv->bt_dev.m_dev[i].name, proc_mbt);
priv->dev_proc[i].proc_entry = NULL;
if (priv->dev_proc[i].pfiles) {
for (j = 0;
j < priv->dev_proc[i].num_proc_files;
j++) {
if (priv->dev_proc[i].pfiles[j].pdata) {
kfree(priv->dev_proc[i].
pfiles[j].pdata);
priv->dev_proc[i].pfiles[j].
pdata = NULL;
}
}
kfree(priv->dev_proc[i].pfiles);
priv->dev_proc[i].pfiles = NULL;
}
}
}
LEAVE();
return;
}
/**
* @brief This function creates proc interface
* directory structure
*
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
int
bt_root_proc_init(void)
{
PRINTM(INFO, "BT: Create Proc Interface\n");
proc_mbt = proc_mkdir("mbt", PROC_DIR);
if (!proc_mbt) {
PRINTM(ERROR, "BT: Cannot create proc interface\n");
return BT_STATUS_FAILURE;
}
return BT_STATUS_SUCCESS;
}
/**
* @brief This function removes proc interface
* directory structure
*
* @return BT_STATUS_SUCCESS
*/
int
bt_root_proc_remove(void)
{
remove_proc_entry("mbt", PROC_DIR);
proc_mbt = NULL;
return BT_STATUS_SUCCESS;
}