Skip to content

Commit

Permalink
wlcore: access the firmware memory via debugfs
Browse files Browse the repository at this point in the history
Applications running in the user space needs access to the
memory of the chip. Examples of such access
- read/write global variables
- access to firmware log
- dump memory after firmware panic event

Arbitrary 4-bytes aligned location can be accessed by
read/write file wlcore/mem

[Check return value of wlcore_raw_read/write and wlcore_set_partition
calls as required by the recent IO changes. -- Luca]

Signed-off-by: Arkady Miasnikov <a-miasnikov@ti.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
  • Loading branch information
Arkady Miasnikov authored and Luciano Coelho committed Jun 22, 2012
1 parent f1a26e6 commit e1262ef
Showing 1 changed file with 192 additions and 0 deletions.
192 changes: 192 additions & 0 deletions drivers/net/wireless/ti/wlcore/debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
/* ms */
#define WL1271_DEBUGFS_STATS_LIFETIME 1000

#define WLCORE_MAX_BLOCK_SIZE ((size_t)(4*PAGE_SIZE))

/* debugfs macros idea from mac80211 */
int wl1271_format_buffer(char __user *userbuf, size_t count,
loff_t *ppos, char *fmt, ...)
Expand Down Expand Up @@ -1025,6 +1027,195 @@ static const struct file_operations sleep_auth_ops = {
.llseek = default_llseek,
};

static ssize_t dev_mem_read(struct file *file,
char __user *user_buf, size_t count,
loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
struct wlcore_partition_set part, old_part;
size_t bytes = count;
int ret;
char *buf;

/* only requests of dword-aligned size and offset are supported */
if (bytes % 4)
return -EINVAL;

if (*ppos % 4)
return -EINVAL;

/* function should return in reasonable time */
bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE);

if (bytes == 0)
return -EINVAL;

memset(&part, 0, sizeof(part));
part.mem.start = file->f_pos;
part.mem.size = bytes;

buf = kmalloc(bytes, GFP_KERNEL);
if (!buf)
return -ENOMEM;

mutex_lock(&wl->mutex);

if (wl->state == WL1271_STATE_OFF) {
ret = -EFAULT;
goto skip_read;
}

ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto skip_read;

/* store current partition and switch partition */
memcpy(&old_part, &wl->curr_part, sizeof(old_part));
ret = wlcore_set_partition(wl, &part);
if (ret < 0)
goto part_err;

ret = wlcore_raw_read(wl, 0, buf, bytes, false);
if (ret < 0)
goto read_err;

read_err:
/* recover partition */
ret = wlcore_set_partition(wl, &old_part);
if (ret < 0)
goto part_err;

part_err:
wl1271_ps_elp_sleep(wl);

skip_read:
mutex_unlock(&wl->mutex);

if (ret == 0) {
ret = copy_to_user(user_buf, buf, bytes);
if (ret < bytes) {
bytes -= ret;
*ppos += bytes;
ret = 0;
} else {
ret = -EFAULT;
}
}

kfree(buf);

return ((ret == 0) ? bytes : ret);
}

static ssize_t dev_mem_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
struct wlcore_partition_set part, old_part;
size_t bytes = count;
int ret;
char *buf;

/* only requests of dword-aligned size and offset are supported */
if (bytes % 4)
return -EINVAL;

if (*ppos % 4)
return -EINVAL;

/* function should return in reasonable time */
bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE);

if (bytes == 0)
return -EINVAL;

memset(&part, 0, sizeof(part));
part.mem.start = file->f_pos;
part.mem.size = bytes;

buf = kmalloc(bytes, GFP_KERNEL);
if (!buf)
return -ENOMEM;

ret = copy_from_user(buf, user_buf, bytes);
if (ret) {
ret = -EFAULT;
goto err_out;
}

mutex_lock(&wl->mutex);

if (wl->state == WL1271_STATE_OFF) {
ret = -EFAULT;
goto skip_write;
}

ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto skip_write;

/* store current partition and switch partition */
memcpy(&old_part, &wl->curr_part, sizeof(old_part));
ret = wlcore_set_partition(wl, &part);
if (ret < 0)
goto part_err;

ret = wlcore_raw_write(wl, 0, buf, bytes, false);
if (ret < 0)
goto write_err;

write_err:
/* recover partition */
ret = wlcore_set_partition(wl, &old_part);
if (ret < 0)
goto part_err;

part_err:
wl1271_ps_elp_sleep(wl);

skip_write:
mutex_unlock(&wl->mutex);

if (ret == 0)
*ppos += bytes;

err_out:
kfree(buf);

return ((ret == 0) ? bytes : ret);
}

static loff_t dev_mem_seek(struct file *file, loff_t offset, int orig)
{
loff_t ret;

/* only requests of dword-aligned size and offset are supported */
if (offset % 4)
return -EINVAL;

switch (orig) {
case SEEK_SET:
file->f_pos = offset;
ret = file->f_pos;
break;
case SEEK_CUR:
file->f_pos += offset;
ret = file->f_pos;
break;
default:
ret = -EINVAL;
}

return ret;
}

static const struct file_operations dev_mem_ops = {
.open = simple_open,
.read = dev_mem_read,
.write = dev_mem_write,
.llseek = dev_mem_seek,
};

static int wl1271_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir)
{
Expand Down Expand Up @@ -1059,6 +1250,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming);
DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming);

DEBUGFS_ADD_PREFIX(dev, mem, rootdir);

return 0;

Expand Down

0 comments on commit e1262ef

Please sign in to comment.