|
@@ -965,6 +965,84 @@ static void qlcnic_set_msglevel(struct net_device *netdev, u32 msglvl)
|
|
|
adapter->msg_enable = msglvl;
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+qlcnic_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump)
|
|
|
+{
|
|
|
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
|
|
|
+ struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
|
|
|
+
|
|
|
+ dump->len = fw_dump->tmpl_hdr->size + fw_dump->size;
|
|
|
+ dump->flag = fw_dump->tmpl_hdr->drv_cap_mask;
|
|
|
+ dump->version = adapter->fw_version;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
|
|
|
+ void *buffer)
|
|
|
+{
|
|
|
+ int i, copy_sz;
|
|
|
+ u32 *hdr_ptr, *data;
|
|
|
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
|
|
|
+ struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
|
|
|
+
|
|
|
+ if (qlcnic_api_lock(adapter))
|
|
|
+ return -EIO;
|
|
|
+ if (!fw_dump->clr) {
|
|
|
+ netdev_info(netdev, "Dump not available\n");
|
|
|
+ qlcnic_api_unlock(adapter);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ /* Copy template header first */
|
|
|
+ copy_sz = fw_dump->tmpl_hdr->size;
|
|
|
+ hdr_ptr = (u32 *) fw_dump->tmpl_hdr;
|
|
|
+ data = (u32 *) buffer;
|
|
|
+ for (i = 0; i < copy_sz/sizeof(u32); i++)
|
|
|
+ *data++ = cpu_to_le32(*hdr_ptr++);
|
|
|
+
|
|
|
+ /* Copy captured dump data */
|
|
|
+ memcpy(buffer + copy_sz, fw_dump->data, fw_dump->size);
|
|
|
+ dump->len = copy_sz + fw_dump->size;
|
|
|
+ dump->flag = fw_dump->tmpl_hdr->drv_cap_mask;
|
|
|
+
|
|
|
+ /* Free dump area once data has been captured */
|
|
|
+ vfree(fw_dump->data);
|
|
|
+ fw_dump->data = NULL;
|
|
|
+ fw_dump->clr = 0;
|
|
|
+ qlcnic_api_unlock(adapter);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
|
|
|
+ struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
|
|
|
+
|
|
|
+ if (val->flag == QLCNIC_FORCE_FW_DUMP_KEY) {
|
|
|
+ netdev_info(netdev, "Forcing a FW dump\n");
|
|
|
+ qlcnic_dev_request_reset(adapter);
|
|
|
+ } else {
|
|
|
+ if (val->flag > QLCNIC_DUMP_MASK_MAX ||
|
|
|
+ val->flag < QLCNIC_DUMP_MASK_MIN) {
|
|
|
+ netdev_info(netdev,
|
|
|
+ "Invalid dump level: 0x%x\n", val->flag);
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ if (qlcnic_api_lock(adapter))
|
|
|
+ return -EIO;
|
|
|
+ fw_dump->tmpl_hdr->drv_cap_mask = val->flag & 0xff;
|
|
|
+ qlcnic_api_unlock(adapter);
|
|
|
+ netdev_info(netdev, "Driver mask changed to: 0x%x\n",
|
|
|
+ fw_dump->tmpl_hdr->drv_cap_mask);
|
|
|
+ }
|
|
|
+out:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
const struct ethtool_ops qlcnic_ethtool_ops = {
|
|
|
.get_settings = qlcnic_get_settings,
|
|
|
.set_settings = qlcnic_set_settings,
|
|
@@ -991,4 +1069,7 @@ const struct ethtool_ops qlcnic_ethtool_ops = {
|
|
|
.set_phys_id = qlcnic_set_led,
|
|
|
.set_msglevel = qlcnic_set_msglevel,
|
|
|
.get_msglevel = qlcnic_get_msglevel,
|
|
|
+ .get_dump_flag = qlcnic_get_dump_flag,
|
|
|
+ .get_dump_data = qlcnic_get_dump_data,
|
|
|
+ .set_dump = qlcnic_set_dump,
|
|
|
};
|