diff options
author | Arkady Miasnikov <a-miasnikov@ti.com> | 2012-06-18 09:21:12 -0400 |
---|---|---|
committer | Luciano Coelho <coelho@ti.com> | 2012-06-22 15:52:01 -0400 |
commit | e1262efb9bf9864532c0dfca2b2e222aee7bd0a5 (patch) | |
tree | 6d674f6d8e3d0102261d53bc787b3c171fc4e8ec /drivers/net | |
parent | f1a26e638e646d971f77c5a5186ee254b3f4e818 (diff) |
wlcore: access the firmware memory via debugfs
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>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/ti/wlcore/debugfs.c | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 1768f37049bd..80dbc5304fac 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c | |||
@@ -38,6 +38,8 @@ | |||
38 | /* ms */ | 38 | /* ms */ |
39 | #define WL1271_DEBUGFS_STATS_LIFETIME 1000 | 39 | #define WL1271_DEBUGFS_STATS_LIFETIME 1000 |
40 | 40 | ||
41 | #define WLCORE_MAX_BLOCK_SIZE ((size_t)(4*PAGE_SIZE)) | ||
42 | |||
41 | /* debugfs macros idea from mac80211 */ | 43 | /* debugfs macros idea from mac80211 */ |
42 | int wl1271_format_buffer(char __user *userbuf, size_t count, | 44 | int wl1271_format_buffer(char __user *userbuf, size_t count, |
43 | loff_t *ppos, char *fmt, ...) | 45 | loff_t *ppos, char *fmt, ...) |
@@ -1025,6 +1027,195 @@ static const struct file_operations sleep_auth_ops = { | |||
1025 | .llseek = default_llseek, | 1027 | .llseek = default_llseek, |
1026 | }; | 1028 | }; |
1027 | 1029 | ||
1030 | static ssize_t dev_mem_read(struct file *file, | ||
1031 | char __user *user_buf, size_t count, | ||
1032 | loff_t *ppos) | ||
1033 | { | ||
1034 | struct wl1271 *wl = file->private_data; | ||
1035 | struct wlcore_partition_set part, old_part; | ||
1036 | size_t bytes = count; | ||
1037 | int ret; | ||
1038 | char *buf; | ||
1039 | |||
1040 | /* only requests of dword-aligned size and offset are supported */ | ||
1041 | if (bytes % 4) | ||
1042 | return -EINVAL; | ||
1043 | |||
1044 | if (*ppos % 4) | ||
1045 | return -EINVAL; | ||
1046 | |||
1047 | /* function should return in reasonable time */ | ||
1048 | bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE); | ||
1049 | |||
1050 | if (bytes == 0) | ||
1051 | return -EINVAL; | ||
1052 | |||
1053 | memset(&part, 0, sizeof(part)); | ||
1054 | part.mem.start = file->f_pos; | ||
1055 | part.mem.size = bytes; | ||
1056 | |||
1057 | buf = kmalloc(bytes, GFP_KERNEL); | ||
1058 | if (!buf) | ||
1059 | return -ENOMEM; | ||
1060 | |||
1061 | mutex_lock(&wl->mutex); | ||
1062 | |||
1063 | if (wl->state == WL1271_STATE_OFF) { | ||
1064 | ret = -EFAULT; | ||
1065 | goto skip_read; | ||
1066 | } | ||
1067 | |||
1068 | ret = wl1271_ps_elp_wakeup(wl); | ||
1069 | if (ret < 0) | ||
1070 | goto skip_read; | ||
1071 | |||
1072 | /* store current partition and switch partition */ | ||
1073 | memcpy(&old_part, &wl->curr_part, sizeof(old_part)); | ||
1074 | ret = wlcore_set_partition(wl, &part); | ||
1075 | if (ret < 0) | ||
1076 | goto part_err; | ||
1077 | |||
1078 | ret = wlcore_raw_read(wl, 0, buf, bytes, false); | ||
1079 | if (ret < 0) | ||
1080 | goto read_err; | ||
1081 | |||
1082 | read_err: | ||
1083 | /* recover partition */ | ||
1084 | ret = wlcore_set_partition(wl, &old_part); | ||
1085 | if (ret < 0) | ||
1086 | goto part_err; | ||
1087 | |||
1088 | part_err: | ||
1089 | wl1271_ps_elp_sleep(wl); | ||
1090 | |||
1091 | skip_read: | ||
1092 | mutex_unlock(&wl->mutex); | ||
1093 | |||
1094 | if (ret == 0) { | ||
1095 | ret = copy_to_user(user_buf, buf, bytes); | ||
1096 | if (ret < bytes) { | ||
1097 | bytes -= ret; | ||
1098 | *ppos += bytes; | ||
1099 | ret = 0; | ||
1100 | } else { | ||
1101 | ret = -EFAULT; | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | kfree(buf); | ||
1106 | |||
1107 | return ((ret == 0) ? bytes : ret); | ||
1108 | } | ||
1109 | |||
1110 | static ssize_t dev_mem_write(struct file *file, const char __user *user_buf, | ||
1111 | size_t count, loff_t *ppos) | ||
1112 | { | ||
1113 | struct wl1271 *wl = file->private_data; | ||
1114 | struct wlcore_partition_set part, old_part; | ||
1115 | size_t bytes = count; | ||
1116 | int ret; | ||
1117 | char *buf; | ||
1118 | |||
1119 | /* only requests of dword-aligned size and offset are supported */ | ||
1120 | if (bytes % 4) | ||
1121 | return -EINVAL; | ||
1122 | |||
1123 | if (*ppos % 4) | ||
1124 | return -EINVAL; | ||
1125 | |||
1126 | /* function should return in reasonable time */ | ||
1127 | bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE); | ||
1128 | |||
1129 | if (bytes == 0) | ||
1130 | return -EINVAL; | ||
1131 | |||
1132 | memset(&part, 0, sizeof(part)); | ||
1133 | part.mem.start = file->f_pos; | ||
1134 | part.mem.size = bytes; | ||
1135 | |||
1136 | buf = kmalloc(bytes, GFP_KERNEL); | ||
1137 | if (!buf) | ||
1138 | return -ENOMEM; | ||
1139 | |||
1140 | ret = copy_from_user(buf, user_buf, bytes); | ||
1141 | if (ret) { | ||
1142 | ret = -EFAULT; | ||
1143 | goto err_out; | ||
1144 | } | ||
1145 | |||
1146 | mutex_lock(&wl->mutex); | ||
1147 | |||
1148 | if (wl->state == WL1271_STATE_OFF) { | ||
1149 | ret = -EFAULT; | ||
1150 | goto skip_write; | ||
1151 | } | ||
1152 | |||
1153 | ret = wl1271_ps_elp_wakeup(wl); | ||
1154 | if (ret < 0) | ||
1155 | goto skip_write; | ||
1156 | |||
1157 | /* store current partition and switch partition */ | ||
1158 | memcpy(&old_part, &wl->curr_part, sizeof(old_part)); | ||
1159 | ret = wlcore_set_partition(wl, &part); | ||
1160 | if (ret < 0) | ||
1161 | goto part_err; | ||
1162 | |||
1163 | ret = wlcore_raw_write(wl, 0, buf, bytes, false); | ||
1164 | if (ret < 0) | ||
1165 | goto write_err; | ||
1166 | |||
1167 | write_err: | ||
1168 | /* recover partition */ | ||
1169 | ret = wlcore_set_partition(wl, &old_part); | ||
1170 | if (ret < 0) | ||
1171 | goto part_err; | ||
1172 | |||
1173 | part_err: | ||
1174 | wl1271_ps_elp_sleep(wl); | ||
1175 | |||
1176 | skip_write: | ||
1177 | mutex_unlock(&wl->mutex); | ||
1178 | |||
1179 | if (ret == 0) | ||
1180 | *ppos += bytes; | ||
1181 | |||
1182 | err_out: | ||
1183 | kfree(buf); | ||
1184 | |||
1185 | return ((ret == 0) ? bytes : ret); | ||
1186 | } | ||
1187 | |||
1188 | static loff_t dev_mem_seek(struct file *file, loff_t offset, int orig) | ||
1189 | { | ||
1190 | loff_t ret; | ||
1191 | |||
1192 | /* only requests of dword-aligned size and offset are supported */ | ||
1193 | if (offset % 4) | ||
1194 | return -EINVAL; | ||
1195 | |||
1196 | switch (orig) { | ||
1197 | case SEEK_SET: | ||
1198 | file->f_pos = offset; | ||
1199 | ret = file->f_pos; | ||
1200 | break; | ||
1201 | case SEEK_CUR: | ||
1202 | file->f_pos += offset; | ||
1203 | ret = file->f_pos; | ||
1204 | break; | ||
1205 | default: | ||
1206 | ret = -EINVAL; | ||
1207 | } | ||
1208 | |||
1209 | return ret; | ||
1210 | } | ||
1211 | |||
1212 | static const struct file_operations dev_mem_ops = { | ||
1213 | .open = simple_open, | ||
1214 | .read = dev_mem_read, | ||
1215 | .write = dev_mem_write, | ||
1216 | .llseek = dev_mem_seek, | ||
1217 | }; | ||
1218 | |||
1028 | static int wl1271_debugfs_add_files(struct wl1271 *wl, | 1219 | static int wl1271_debugfs_add_files(struct wl1271 *wl, |
1029 | struct dentry *rootdir) | 1220 | struct dentry *rootdir) |
1030 | { | 1221 | { |
@@ -1059,6 +1250,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, | |||
1059 | DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming); | 1250 | DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming); |
1060 | DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming); | 1251 | DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming); |
1061 | 1252 | ||
1253 | DEBUGFS_ADD_PREFIX(dev, mem, rootdir); | ||
1062 | 1254 | ||
1063 | return 0; | 1255 | return 0; |
1064 | 1256 | ||