diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ipw2200.c | 120 | ||||
-rw-r--r-- | drivers/net/wireless/ipw2200.h | 11 |
2 files changed, 117 insertions, 14 deletions
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index ef1007785030..bcb5993b68bd 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c | |||
@@ -44,6 +44,7 @@ MODULE_VERSION(DRV_VERSION); | |||
44 | MODULE_AUTHOR(DRV_COPYRIGHT); | 44 | MODULE_AUTHOR(DRV_COPYRIGHT); |
45 | MODULE_LICENSE("GPL"); | 45 | MODULE_LICENSE("GPL"); |
46 | 46 | ||
47 | static int cmdlog = 0; | ||
47 | static int debug = 0; | 48 | static int debug = 0; |
48 | static int channel = 0; | 49 | static int channel = 0; |
49 | static int mode = 0; | 50 | static int mode = 0; |
@@ -148,8 +149,8 @@ static int init_supported_rates(struct ipw_priv *priv, | |||
148 | static void ipw_set_hwcrypto_keys(struct ipw_priv *); | 149 | static void ipw_set_hwcrypto_keys(struct ipw_priv *); |
149 | static void ipw_send_wep_keys(struct ipw_priv *, int); | 150 | static void ipw_send_wep_keys(struct ipw_priv *, int); |
150 | 151 | ||
151 | static char *snprint_line(char *buf, size_t count, | 152 | static int snprint_line(char *buf, size_t count, |
152 | const u8 * data, u32 len, u32 ofs) | 153 | const u8 * data, u32 len, u32 ofs) |
153 | { | 154 | { |
154 | int out, i, j, l; | 155 | int out, i, j, l; |
155 | char c; | 156 | char c; |
@@ -180,7 +181,7 @@ static char *snprint_line(char *buf, size_t count, | |||
180 | out += snprintf(buf + out, count - out, " "); | 181 | out += snprintf(buf + out, count - out, " "); |
181 | } | 182 | } |
182 | 183 | ||
183 | return buf; | 184 | return out; |
184 | } | 185 | } |
185 | 186 | ||
186 | static void printk_buf(int level, const u8 * data, u32 len) | 187 | static void printk_buf(int level, const u8 * data, u32 len) |
@@ -191,14 +192,33 @@ static void printk_buf(int level, const u8 * data, u32 len) | |||
191 | return; | 192 | return; |
192 | 193 | ||
193 | while (len) { | 194 | while (len) { |
194 | printk(KERN_DEBUG "%s\n", | 195 | snprint_line(line, sizeof(line), &data[ofs], |
195 | snprint_line(line, sizeof(line), &data[ofs], | 196 | min(len, 16U), ofs); |
196 | min(len, 16U), ofs)); | 197 | printk(KERN_DEBUG "%s\n", line); |
197 | ofs += 16; | 198 | ofs += 16; |
198 | len -= min(len, 16U); | 199 | len -= min(len, 16U); |
199 | } | 200 | } |
200 | } | 201 | } |
201 | 202 | ||
203 | static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len) | ||
204 | { | ||
205 | size_t out = size; | ||
206 | u32 ofs = 0; | ||
207 | int total = 0; | ||
208 | |||
209 | while (size && len) { | ||
210 | out = snprint_line(output, size, &data[ofs], | ||
211 | min_t(size_t, len, 16U), ofs); | ||
212 | |||
213 | ofs += 16; | ||
214 | output += out; | ||
215 | size -= out; | ||
216 | len -= min_t(size_t, len, 16U); | ||
217 | total += out; | ||
218 | } | ||
219 | return total; | ||
220 | } | ||
221 | |||
202 | static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg); | 222 | static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg); |
203 | #define ipw_read_reg32(a, b) _ipw_read_reg32(a, b) | 223 | #define ipw_read_reg32(a, b) _ipw_read_reg32(a, b) |
204 | 224 | ||
@@ -272,9 +292,15 @@ static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs) | |||
272 | #define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs) | 292 | #define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs) |
273 | 293 | ||
274 | static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int); | 294 | static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int); |
275 | #define ipw_read_indirect(a, b, c, d) \ | 295 | static inline void __ipw_read_indirect(const char *f, int l, |
276 | IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \ | 296 | struct ipw_priv *a, u32 b, u8 * c, int d) |
277 | _ipw_read_indirect(a, b, c, d) | 297 | { |
298 | IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", f, l, (u32) (b), | ||
299 | d); | ||
300 | _ipw_read_indirect(a, b, c, d); | ||
301 | } | ||
302 | |||
303 | #define ipw_read_indirect(a, b, c, d) __ipw_read_indirect(__FILE__, __LINE__, a, b, c, d) | ||
278 | 304 | ||
279 | static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data, | 305 | static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data, |
280 | int num); | 306 | int num); |
@@ -1070,6 +1096,7 @@ static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv) | |||
1070 | "failed.\n"); | 1096 | "failed.\n"); |
1071 | return NULL; | 1097 | return NULL; |
1072 | } | 1098 | } |
1099 | error->jiffies = jiffies; | ||
1073 | error->status = priv->status; | 1100 | error->status = priv->status; |
1074 | error->config = priv->config; | 1101 | error->config = priv->config; |
1075 | error->elem_len = elem_len; | 1102 | error->elem_len = elem_len; |
@@ -1122,7 +1149,8 @@ static ssize_t show_error(struct device *d, | |||
1122 | if (!priv->error) | 1149 | if (!priv->error) |
1123 | return 0; | 1150 | return 0; |
1124 | len += snprintf(buf + len, PAGE_SIZE - len, | 1151 | len += snprintf(buf + len, PAGE_SIZE - len, |
1125 | "%08X%08X%08X", | 1152 | "%08lX%08X%08X%08X", |
1153 | priv->error->jiffies, | ||
1126 | priv->error->status, | 1154 | priv->error->status, |
1127 | priv->error->config, priv->error->elem_len); | 1155 | priv->error->config, priv->error->elem_len); |
1128 | for (i = 0; i < priv->error->elem_len; i++) | 1156 | for (i = 0; i < priv->error->elem_len; i++) |
@@ -1162,6 +1190,33 @@ static ssize_t clear_error(struct device *d, | |||
1162 | 1190 | ||
1163 | static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error); | 1191 | static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error); |
1164 | 1192 | ||
1193 | static ssize_t show_cmd_log(struct device *d, | ||
1194 | struct device_attribute *attr, char *buf) | ||
1195 | { | ||
1196 | struct ipw_priv *priv = dev_get_drvdata(d); | ||
1197 | u32 len = 0, i; | ||
1198 | if (!priv->cmdlog) | ||
1199 | return 0; | ||
1200 | for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len; | ||
1201 | (i != priv->cmdlog_pos) && (PAGE_SIZE - len); | ||
1202 | i = (i + 1) % priv->cmdlog_len) { | ||
1203 | len += | ||
1204 | snprintf(buf + len, PAGE_SIZE - len, | ||
1205 | "\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies, | ||
1206 | priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd, | ||
1207 | priv->cmdlog[i].cmd.len); | ||
1208 | len += | ||
1209 | snprintk_buf(buf + len, PAGE_SIZE - len, | ||
1210 | (u8 *) priv->cmdlog[i].cmd.param, | ||
1211 | priv->cmdlog[i].cmd.len); | ||
1212 | len += snprintf(buf + len, PAGE_SIZE - len, "\n"); | ||
1213 | } | ||
1214 | len += snprintf(buf + len, PAGE_SIZE - len, "\n"); | ||
1215 | return len; | ||
1216 | } | ||
1217 | |||
1218 | static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL); | ||
1219 | |||
1165 | static ssize_t show_scan_age(struct device *d, struct device_attribute *attr, | 1220 | static ssize_t show_scan_age(struct device *d, struct device_attribute *attr, |
1166 | char *buf) | 1221 | char *buf) |
1167 | { | 1222 | { |
@@ -1825,6 +1880,15 @@ static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd) | |||
1825 | 1880 | ||
1826 | priv->status |= STATUS_HCMD_ACTIVE; | 1881 | priv->status |= STATUS_HCMD_ACTIVE; |
1827 | 1882 | ||
1883 | if (priv->cmdlog) { | ||
1884 | priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies; | ||
1885 | priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd; | ||
1886 | priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len; | ||
1887 | memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param, | ||
1888 | cmd->len); | ||
1889 | priv->cmdlog[priv->cmdlog_pos].retcode = -1; | ||
1890 | } | ||
1891 | |||
1828 | IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n", | 1892 | IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n", |
1829 | get_cmd_string(cmd->cmd), cmd->cmd, cmd->len, | 1893 | get_cmd_string(cmd->cmd), cmd->cmd, cmd->len, |
1830 | priv->status); | 1894 | priv->status); |
@@ -1836,7 +1900,7 @@ static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd) | |||
1836 | IPW_ERROR("Failed to send %s: Reason %d\n", | 1900 | IPW_ERROR("Failed to send %s: Reason %d\n", |
1837 | get_cmd_string(cmd->cmd), rc); | 1901 | get_cmd_string(cmd->cmd), rc); |
1838 | spin_unlock_irqrestore(&priv->lock, flags); | 1902 | spin_unlock_irqrestore(&priv->lock, flags); |
1839 | return rc; | 1903 | goto exit; |
1840 | } | 1904 | } |
1841 | spin_unlock_irqrestore(&priv->lock, flags); | 1905 | spin_unlock_irqrestore(&priv->lock, flags); |
1842 | 1906 | ||
@@ -1851,7 +1915,8 @@ static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd) | |||
1851 | get_cmd_string(cmd->cmd)); | 1915 | get_cmd_string(cmd->cmd)); |
1852 | priv->status &= ~STATUS_HCMD_ACTIVE; | 1916 | priv->status &= ~STATUS_HCMD_ACTIVE; |
1853 | spin_unlock_irqrestore(&priv->lock, flags); | 1917 | spin_unlock_irqrestore(&priv->lock, flags); |
1854 | return -EIO; | 1918 | rc = -EIO; |
1919 | goto exit; | ||
1855 | } | 1920 | } |
1856 | spin_unlock_irqrestore(&priv->lock, flags); | 1921 | spin_unlock_irqrestore(&priv->lock, flags); |
1857 | } | 1922 | } |
@@ -1859,10 +1924,16 @@ static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd) | |||
1859 | if (priv->status & STATUS_RF_KILL_HW) { | 1924 | if (priv->status & STATUS_RF_KILL_HW) { |
1860 | IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n", | 1925 | IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n", |
1861 | get_cmd_string(cmd->cmd)); | 1926 | get_cmd_string(cmd->cmd)); |
1862 | return -EIO; | 1927 | rc = -EIO; |
1928 | goto exit; | ||
1863 | } | 1929 | } |
1864 | 1930 | ||
1865 | return 0; | 1931 | exit: |
1932 | if (priv->cmdlog) { | ||
1933 | priv->cmdlog[priv->cmdlog_pos++].retcode = rc; | ||
1934 | priv->cmdlog_pos %= priv->cmdlog_len; | ||
1935 | } | ||
1936 | return rc; | ||
1866 | } | 1937 | } |
1867 | 1938 | ||
1868 | static int ipw_send_host_complete(struct ipw_priv *priv) | 1939 | static int ipw_send_host_complete(struct ipw_priv *priv) |
@@ -10712,6 +10783,18 @@ static int ipw_up(struct ipw_priv *priv) | |||
10712 | if (priv->status & STATUS_EXIT_PENDING) | 10783 | if (priv->status & STATUS_EXIT_PENDING) |
10713 | return -EIO; | 10784 | return -EIO; |
10714 | 10785 | ||
10786 | if (cmdlog && !priv->cmdlog) { | ||
10787 | priv->cmdlog = kmalloc(sizeof(*priv->cmdlog) * cmdlog, | ||
10788 | GFP_KERNEL); | ||
10789 | if (priv->cmdlog == NULL) { | ||
10790 | IPW_ERROR("Error allocating %d command log entries.\n", | ||
10791 | cmdlog); | ||
10792 | } else { | ||
10793 | memset(priv->cmdlog, 0, sizeof(*priv->cmdlog) * cmdlog); | ||
10794 | priv->cmdlog_len = cmdlog; | ||
10795 | } | ||
10796 | } | ||
10797 | |||
10715 | for (i = 0; i < MAX_HW_RESTARTS; i++) { | 10798 | for (i = 0; i < MAX_HW_RESTARTS; i++) { |
10716 | /* Load the microcode, firmware, and eeprom. | 10799 | /* Load the microcode, firmware, and eeprom. |
10717 | * Also start the clocks. */ | 10800 | * Also start the clocks. */ |
@@ -10935,6 +11018,7 @@ static struct attribute *ipw_sysfs_entries[] = { | |||
10935 | &dev_attr_cfg.attr, | 11018 | &dev_attr_cfg.attr, |
10936 | &dev_attr_error.attr, | 11019 | &dev_attr_error.attr, |
10937 | &dev_attr_event_log.attr, | 11020 | &dev_attr_event_log.attr, |
11021 | &dev_attr_cmd_log.attr, | ||
10938 | &dev_attr_eeprom_delay.attr, | 11022 | &dev_attr_eeprom_delay.attr, |
10939 | &dev_attr_ucode_version.attr, | 11023 | &dev_attr_ucode_version.attr, |
10940 | &dev_attr_rtc.attr, | 11024 | &dev_attr_rtc.attr, |
@@ -11129,6 +11213,10 @@ static void ipw_pci_remove(struct pci_dev *pdev) | |||
11129 | } | 11213 | } |
11130 | ipw_tx_queue_free(priv); | 11214 | ipw_tx_queue_free(priv); |
11131 | 11215 | ||
11216 | if (priv->cmdlog) { | ||
11217 | kfree(priv->cmdlog); | ||
11218 | priv->cmdlog = NULL; | ||
11219 | } | ||
11132 | /* ipw_down will ensure that there is no more pending work | 11220 | /* ipw_down will ensure that there is no more pending work |
11133 | * in the workqueue's, so we can safely remove them now. */ | 11221 | * in the workqueue's, so we can safely remove them now. */ |
11134 | cancel_delayed_work(&priv->adhoc_check); | 11222 | cancel_delayed_work(&priv->adhoc_check); |
@@ -11302,5 +11390,9 @@ MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)"); | |||
11302 | module_param(hwcrypto, int, 0444); | 11390 | module_param(hwcrypto, int, 0444); |
11303 | MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default on)"); | 11391 | MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default on)"); |
11304 | 11392 | ||
11393 | module_param(cmdlog, int, 0444); | ||
11394 | MODULE_PARM_DESC(cmdlog, | ||
11395 | "allocate a ring buffer for logging firmware commands"); | ||
11396 | |||
11305 | module_exit(ipw_exit); | 11397 | module_exit(ipw_exit); |
11306 | module_init(ipw_init); | 11398 | module_init(ipw_init); |
diff --git a/drivers/net/wireless/ipw2200.h b/drivers/net/wireless/ipw2200.h index 9bf03ac55390..255f0cb66f64 100644 --- a/drivers/net/wireless/ipw2200.h +++ b/drivers/net/wireless/ipw2200.h | |||
@@ -1102,6 +1102,7 @@ struct ipw_event { | |||
1102 | } __attribute__ ((packed)); | 1102 | } __attribute__ ((packed)); |
1103 | 1103 | ||
1104 | struct ipw_fw_error { | 1104 | struct ipw_fw_error { |
1105 | unsigned long jiffies; | ||
1105 | u32 status; | 1106 | u32 status; |
1106 | u32 config; | 1107 | u32 config; |
1107 | u32 elem_len; | 1108 | u32 elem_len; |
@@ -1261,6 +1262,10 @@ struct ipw_priv { | |||
1261 | struct work_struct led_act_off; | 1262 | struct work_struct led_act_off; |
1262 | struct work_struct merge_networks; | 1263 | struct work_struct merge_networks; |
1263 | 1264 | ||
1265 | struct ipw_cmd_log *cmdlog; | ||
1266 | int cmdlog_len; | ||
1267 | int cmdlog_pos; | ||
1268 | |||
1264 | #define IPW_2200BG 1 | 1269 | #define IPW_2200BG 1 |
1265 | #define IPW_2915ABG 2 | 1270 | #define IPW_2915ABG 2 |
1266 | u8 adapter; | 1271 | u8 adapter; |
@@ -1853,6 +1858,12 @@ struct host_cmd { | |||
1853 | u32 param[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH]; | 1858 | u32 param[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH]; |
1854 | } __attribute__ ((packed)); | 1859 | } __attribute__ ((packed)); |
1855 | 1860 | ||
1861 | struct ipw_cmd_log { | ||
1862 | unsigned long jiffies; | ||
1863 | int retcode; | ||
1864 | struct host_cmd cmd; | ||
1865 | }; | ||
1866 | |||
1856 | #define CFG_BT_COEXISTENCE_MIN 0x00 | 1867 | #define CFG_BT_COEXISTENCE_MIN 0x00 |
1857 | #define CFG_BT_COEXISTENCE_DEFER 0x02 | 1868 | #define CFG_BT_COEXISTENCE_DEFER 0x02 |
1858 | #define CFG_BT_COEXISTENCE_KILL 0x04 | 1869 | #define CFG_BT_COEXISTENCE_KILL 0x04 |