wil6210: add support for device led configuration
authorMaya Erez <qca_merez@qca.qualcomm.com>
Mon, 9 May 2016 18:57:11 +0000 (21:57 +0300)
committerKalle Valo <kvalo@qca.qualcomm.com>
Wed, 11 May 2016 19:45:20 +0000 (22:45 +0300)
Add the ability to configure the device led to be used for notifying
the AP activity (60G device supports leds 0-2).
The host can also configure the blinking frequency of the led in
three states.

Signed-off-by: Maya Erez <qca_merez@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/ath/wil6210/wmi.h

index 5d8823d..a8098b4 100644 (file)
@@ -1441,6 +1441,118 @@ static const struct file_operations fops_sta = {
        .llseek         = seq_lseek,
 };
 
+static ssize_t wil_read_file_led_cfg(struct file *file, char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       char buf[80];
+       int n;
+
+       n = snprintf(buf, sizeof(buf),
+                    "led_id is set to %d, echo 1 to enable, 0 to disable\n",
+                    led_id);
+
+       n = min_t(int, n, sizeof(buf));
+
+       return simple_read_from_buffer(user_buf, count, ppos,
+                                      buf, n);
+}
+
+static ssize_t wil_write_file_led_cfg(struct file *file,
+                                     const char __user *buf_,
+                                     size_t count, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       int val;
+       int rc;
+
+       rc = kstrtoint_from_user(buf_, count, 0, &val);
+       if (rc) {
+               wil_err(wil, "Invalid argument\n");
+               return rc;
+       }
+
+       wil_info(wil, "%s led %d\n", val ? "Enabling" : "Disabling", led_id);
+       rc = wmi_led_cfg(wil, val);
+       if (rc) {
+               wil_info(wil, "%s led %d failed\n",
+                        val ? "Enabling" : "Disabling", led_id);
+               return rc;
+       }
+
+       return count;
+}
+
+static const struct file_operations fops_led_cfg = {
+       .read = wil_read_file_led_cfg,
+       .write = wil_write_file_led_cfg,
+       .open  = simple_open,
+};
+
+/* led_blink_time, write:
+ * "<blink_on_slow> <blink_off_slow> <blink_on_med> <blink_off_med> <blink_on_fast> <blink_off_fast>
+ */
+static ssize_t wil_write_led_blink_time(struct file *file,
+                                       const char __user *buf,
+                                       size_t len, loff_t *ppos)
+{
+       int rc;
+       char *kbuf = kmalloc(len + 1, GFP_KERNEL);
+
+       if (!kbuf)
+               return -ENOMEM;
+
+       rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
+       if (rc != len) {
+               kfree(kbuf);
+               return rc >= 0 ? -EIO : rc;
+       }
+
+       kbuf[len] = '\0';
+       rc = sscanf(kbuf, "%d %d %d %d %d %d",
+                   &led_blink_time[WIL_LED_TIME_SLOW].on_ms,
+                   &led_blink_time[WIL_LED_TIME_SLOW].off_ms,
+                   &led_blink_time[WIL_LED_TIME_MED].on_ms,
+                   &led_blink_time[WIL_LED_TIME_MED].off_ms,
+                   &led_blink_time[WIL_LED_TIME_FAST].on_ms,
+                   &led_blink_time[WIL_LED_TIME_FAST].off_ms);
+       kfree(kbuf);
+
+       if (rc < 0)
+               return rc;
+       if (rc < 6)
+               return -EINVAL;
+
+       return len;
+}
+
+static ssize_t wil_read_led_blink_time(struct file *file, char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       static char text[400];
+
+       snprintf(text, sizeof(text),
+                "To set led blink on/off time variables write:\n"
+                "<blink_on_slow> <blink_off_slow> <blink_on_med> "
+                "<blink_off_med> <blink_on_fast> <blink_off_fast>\n"
+                "The current values are:\n"
+                "%d %d %d %d %d %d\n",
+                led_blink_time[WIL_LED_TIME_SLOW].on_ms,
+                led_blink_time[WIL_LED_TIME_SLOW].off_ms,
+                led_blink_time[WIL_LED_TIME_MED].on_ms,
+                led_blink_time[WIL_LED_TIME_MED].off_ms,
+                led_blink_time[WIL_LED_TIME_FAST].on_ms,
+                led_blink_time[WIL_LED_TIME_FAST].off_ms);
+
+       return simple_read_from_buffer(user_buf, count, ppos, text,
+                                      sizeof(text));
+}
+
+static const struct file_operations fops_led_blink_time = {
+       .read = wil_read_led_blink_time,
+       .write = wil_write_led_blink_time,
+       .open  = simple_open,
+};
+
 /*----------------*/
 static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
                                       struct dentry *dbg)
@@ -1489,6 +1601,8 @@ static const struct {
        {"link",        S_IRUGO,                &fops_link},
        {"info",        S_IRUGO,                &fops_info},
        {"recovery",    S_IRUGO | S_IWUSR,      &fops_recovery},
+       {"led_cfg",     S_IRUGO | S_IWUSR,      &fops_led_cfg},
+       {"led_blink_time",      S_IRUGO | S_IWUSR,      &fops_led_blink_time},
 };
 
 static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
@@ -1551,6 +1665,7 @@ static const struct dbg_off dbg_statics[] = {
        {"mem_addr",    S_IRUGO | S_IWUSR, (ulong)&mem_addr, doff_u32},
        {"vring_idle_trsh", S_IRUGO | S_IWUSR, (ulong)&vring_idle_trsh,
         doff_u32},
+       {"led_polarity", S_IRUGO | S_IWUSR, (ulong)&led_polarity, doff_u8},
        {},
 };
 
index f7ed651..8e31d75 100644 (file)
@@ -841,6 +841,9 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
        wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
        wil_bcast_fini(wil);
 
+       /* Disable device led before reset*/
+       wmi_led_cfg(wil, false);
+
        /* prevent NAPI from being scheduled and prevent wmi commands */
        mutex_lock(&wil->wmi_mutex);
        bitmap_zero(wil->status, wil_status_last);
index 74bc2c5..aa09cbc 100644 (file)
@@ -546,6 +546,30 @@ struct wil_blob_wrapper {
        struct debugfs_blob_wrapper blob;
 };
 
+#define WIL_LED_MAX_ID                 (2)
+#define WIL_LED_INVALID_ID             (0xF)
+#define WIL_LED_BLINK_ON_SLOW_MS       (300)
+#define WIL_LED_BLINK_OFF_SLOW_MS      (300)
+#define WIL_LED_BLINK_ON_MED_MS                (200)
+#define WIL_LED_BLINK_OFF_MED_MS       (200)
+#define WIL_LED_BLINK_ON_FAST_MS       (100)
+#define WIL_LED_BLINK_OFF_FAST_MS      (100)
+enum {
+       WIL_LED_TIME_SLOW = 0,
+       WIL_LED_TIME_MED,
+       WIL_LED_TIME_FAST,
+       WIL_LED_TIME_LAST,
+};
+
+struct blink_on_off_time {
+       u32 on_ms;
+       u32 off_ms;
+};
+
+extern struct blink_on_off_time led_blink_time[WIL_LED_TIME_LAST];
+extern u8 led_id;
+extern u8 led_polarity;
+
 struct wil6210_priv {
        struct pci_dev *pdev;
        struct wireless_dev *wdev;
@@ -834,6 +858,7 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
 int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
                  u8 chan, u8 hidden_ssid, u8 is_go);
 int wmi_pcp_stop(struct wil6210_priv *wil);
+int wmi_led_cfg(struct wil6210_priv *wil, bool enable);
 void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
                        u16 reason_code, bool from_event);
 void wil_probe_client_flush(struct wil6210_priv *wil);
index 2e347ef..b80c5d8 100644 (file)
@@ -32,6 +32,11 @@ module_param(agg_wsize, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(agg_wsize, " Window size for Tx Block Ack after connect;"
                 " 0 - use default; < 0 - don't auto-establish");
 
+u8 led_id = WIL_LED_INVALID_ID;
+module_param(led_id, byte, S_IRUGO);
+MODULE_PARM_DESC(led_id,
+                " 60G device led enablement. Set the led ID (0-2) to enable");
+
 /**
  * WMI event receiving - theory of operations
  *
@@ -94,6 +99,14 @@ const struct fw_map fw_mapping[] = {
         */
 };
 
+struct blink_on_off_time led_blink_time[] = {
+       {WIL_LED_BLINK_ON_SLOW_MS, WIL_LED_BLINK_OFF_SLOW_MS},
+       {WIL_LED_BLINK_ON_MED_MS, WIL_LED_BLINK_OFF_MED_MS},
+       {WIL_LED_BLINK_ON_FAST_MS, WIL_LED_BLINK_OFF_FAST_MS},
+};
+
+u8 led_polarity = LED_POLARITY_LOW_ACTIVE;
+
 /**
  * return AHB address for given firmware/ucode internal (linker) address
  * @x - internal address
@@ -971,6 +984,60 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr)
        return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd));
 }
 
+int wmi_led_cfg(struct wil6210_priv *wil, bool enable)
+{
+       int rc = 0;
+       struct wmi_led_cfg_cmd cmd = {
+               .led_mode = enable,
+               .id = led_id,
+               .slow_blink_cfg.blink_on =
+                       cpu_to_le32(led_blink_time[WIL_LED_TIME_SLOW].on_ms),
+               .slow_blink_cfg.blink_off =
+                       cpu_to_le32(led_blink_time[WIL_LED_TIME_SLOW].off_ms),
+               .medium_blink_cfg.blink_on =
+                       cpu_to_le32(led_blink_time[WIL_LED_TIME_MED].on_ms),
+               .medium_blink_cfg.blink_off =
+                       cpu_to_le32(led_blink_time[WIL_LED_TIME_MED].off_ms),
+               .fast_blink_cfg.blink_on =
+                       cpu_to_le32(led_blink_time[WIL_LED_TIME_FAST].on_ms),
+               .fast_blink_cfg.blink_off =
+                       cpu_to_le32(led_blink_time[WIL_LED_TIME_FAST].off_ms),
+               .led_polarity = led_polarity,
+       };
+       struct {
+               struct wmi_cmd_hdr wmi;
+               struct wmi_led_cfg_done_event evt;
+       } __packed reply;
+
+       if (led_id == WIL_LED_INVALID_ID)
+               goto out;
+
+       if (led_id > WIL_LED_MAX_ID) {
+               wil_err(wil, "Invalid led id %d\n", led_id);
+               rc = -EINVAL;
+               goto out;
+       }
+
+       wil_dbg_wmi(wil,
+                   "%s led %d\n",
+                   enable ? "enabling" : "disabling", led_id);
+
+       rc = wmi_call(wil, WMI_LED_CFG_CMDID, &cmd, sizeof(cmd),
+                     WMI_LED_CFG_DONE_EVENTID, &reply, sizeof(reply),
+                     100);
+       if (rc)
+               goto out;
+
+       if (reply.evt.status) {
+               wil_err(wil, "led %d cfg failed with status %d\n",
+                       led_id, le32_to_cpu(reply.evt.status));
+               rc = -EINVAL;
+       }
+
+out:
+       return rc;
+}
+
 int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
                  u8 chan, u8 hidden_ssid, u8 is_go)
 {
@@ -1013,11 +1080,21 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
        if (reply.evt.status != WMI_FW_STATUS_SUCCESS)
                rc = -EINVAL;
 
+       if (wmi_nettype != WMI_NETTYPE_P2P)
+               /* Don't fail due to error in the led configuration */
+               wmi_led_cfg(wil, true);
+
        return rc;
 }
 
 int wmi_pcp_stop(struct wil6210_priv *wil)
 {
+       int rc;
+
+       rc = wmi_led_cfg(wil, false);
+       if (rc)
+               return rc;
+
        return wmi_call(wil, WMI_PCP_STOP_CMDID, NULL, 0,
                        WMI_PCP_STOPPED_EVENTID, NULL, 0, 20);
 }
index 29865e0..685fe0d 100644 (file)
@@ -129,6 +129,7 @@ enum wmi_command_id {
        WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x855,
        WMI_OTP_READ_CMDID                      = 0x856,
        WMI_OTP_WRITE_CMDID                     = 0x857,
+       WMI_LED_CFG_CMDID                       = 0x858,
        /* Performance monitoring commands */
        WMI_BF_CTRL_CMDID                       = 0x862,
        WMI_NOTIFY_REQ_CMDID                    = 0x863,
@@ -868,6 +869,7 @@ enum wmi_event_id {
        WMI_RX_MGMT_PACKET_EVENTID              = 0x1840,
        WMI_TX_MGMT_PACKET_EVENTID              = 0x1841,
        WMI_OTP_READ_RESULT_EVENTID             = 0x1856,
+       WMI_LED_CFG_DONE_EVENTID                = 0x1858,
        /* Performance monitoring events */
        WMI_DATA_PORT_OPEN_EVENTID              = 0x1860,
        WMI_WBE_LINK_DOWN_EVENTID               = 0x1861,
@@ -1349,4 +1351,63 @@ enum wmi_hidden_ssid {
        WMI_HIDDEN_SSID_CLEAR           = 0xFE,
 };
 
+/* WMI_LED_CFG_CMDID
+ *
+ * Configure LED On\Off\Blinking operation
+ *
+ * Returned events:
+ * - WMI_LED_CFG_DONE_EVENTID
+ */
+enum led_mode {
+       LED_DISABLE     = 0x00,
+       LED_ENABLE      = 0x01,
+};
+
+/* The names of the led as
+ * described on HW schemes.
+ */
+enum wmi_led_id {
+       WMI_LED_WLAN    = 0x00,
+       WMI_LED_WPAN    = 0x01,
+       WMI_LED_WWAN    = 0x02,
+};
+
+/* Led polarity mode. */
+enum wmi_led_polarity {
+       LED_POLARITY_HIGH_ACTIVE        = 0x00,
+       LED_POLARITY_LOW_ACTIVE         = 0x01,
+};
+
+/* Combination of on and off
+ * creates the blinking period
+ */
+struct wmi_led_blink_mode {
+       __le32 blink_on;
+       __le32 blink_off;
+} __packed;
+
+/* WMI_LED_CFG_CMDID */
+struct wmi_led_cfg_cmd {
+       /* enum led_mode_e */
+       u8 led_mode;
+       /* enum wmi_led_id_e */
+       u8 id;
+       /* slow speed blinking combination */
+       struct wmi_led_blink_mode slow_blink_cfg;
+       /* medium speed blinking combination */
+       struct wmi_led_blink_mode medium_blink_cfg;
+       /* high speed blinking combination */
+       struct wmi_led_blink_mode fast_blink_cfg;
+       /* polarity of the led */
+       u8 led_polarity;
+       /* reserved */
+       u8 reserved;
+} __packed;
+
+/* WMI_LED_CFG_DONE_EVENTID */
+struct wmi_led_cfg_done_event {
+       /* led config status */
+       __le32 status;
+} __packed;
+
 #endif /* __WILOCITY_WMI_H__ */