This is an automated email from the ASF dual-hosted git repository.

simbit18 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 7bfd6ee33ec wireless/cc1101: Add MSK/4-FSK, dynamic PATABLE ramping, 
and fix IOCTL safety
7bfd6ee33ec is described below

commit 7bfd6ee33ec4461686953b7d868e88d80ef8aba5
Author: Chip L. <[email protected]>
AuthorDate: Wed Mar 11 16:05:34 2026 +0800

    wireless/cc1101: Add MSK/4-FSK, dynamic PATABLE ramping, and fix IOCTL 
safety
    
    This commit overhauls the CC1101 RF driver to address physical hardware
    constraints, prevent register wrap-around overflows, and support accurate
    dBm power scaling via the standard IOCTL interface.
    
    What this change does:
    1. Adds WLIOC_MSK and WLIOC_4FSK modulation support in `ioctl.h` and driver.
    2. Replaces static PATABLE initialization with a dynamic Ramp-up curve
       generator `cc1101_ioctl_apply_power()` based on lab calibration data.
    3. Modifies WLIOC_SETTXPOWER and WLIOC_GETTXPOWER to process actual dBm
       values with a nearest-match algorithm instead of raw array indices.
    4. Removes DEBUGASSERT on user-space IOCTL pointers and replaces them
       with strict -EFAULT checks.
    5. Implements saturation clamping (e.g., mantissa to 256-511) to prevent
       bitrate and frequency deviation calculation overflows.
    6. Rebuilds volatile PATABLE memory upon SLEEP mode wake-up.
    
    Why it is necessary & what it fixes:
    - Fixes severe OOK modulation distortion and FSK spectral splatter caused
      by statically assigned PATABLE indices.
    - Fixes potential kernel panic in flat builds when IOCTL receives a NULL
      pointer from user space.
    - Fixes register wrap-around (silent failures) when users pass
      out-of-bounds baud rate or FDEV values.
    - Prevents RF silence after SLEEP mode due to PATABLE volatility.
    - Resolves inaccurate power output when changing frequencies dynamically.
    
    Impact:
    Changes the behavior of CC1101 TX power and modulation IOCTLs to strictly
    comply with standard `wlioc` definitions. Improves overall driver stability
    and hardware safety.
    
    Signed-off-by: Chip L. <[email protected]>
---
 drivers/wireless/cc1101.c      | 420 ++++++++++++++++++++++++++++++++++++-----
 include/nuttx/wireless/ioctl.h |   4 +-
 2 files changed, 373 insertions(+), 51 deletions(-)

diff --git a/drivers/wireless/cc1101.c b/drivers/wireless/cc1101.c
index 66893072ddd..8df9b5f8d22 100644
--- a/drivers/wireless/cc1101.c
+++ b/drivers/wireless/cc1101.c
@@ -298,6 +298,9 @@ static int cc1101_file_poll(FAR struct file *filep, FAR 
struct pollfd *fds,
 static int cc1101_file_ioctl(FAR struct file *filep, int cmd,
                              unsigned long arg);
 
+static int cc1101_ioctl_apply_power(FAR struct cc1101_dev_s *dev,
+                                    uint8_t pwr_idx);
+
 /****************************************************************************
  * Private Data
  ****************************************************************************/
@@ -315,6 +318,26 @@ static const struct file_operations g_cc1101ops =
   cc1101_file_poll   /* poll */
 };
 
+/* Mapping of physical output power to hardware index (0-7) */
+
+static const int32_t g_cc1101_dbm_table[8] =
+{
+  -30, -20, -15, -10, -5, 0, 5, 10
+};
+
+/* PATABLE laboratory calibration matrix for four main bands
+ * Corresponding order: -30, -20, -15, -10, -5, 0, +5, +10 dBm
+ * Data source: TI DN013 (SWRA151A)
+ */
+
+static const uint8_t g_cc1101_pa_calibration[4][8] =
+{
+  { 0x12, 0x0d, 0x1c, 0x34, 0x69, 0x51, 0x85, 0xc2 }, /* Index 0: 315 MHz */
+  { 0x12, 0x0e, 0x1c, 0x34, 0x69, 0x60, 0x84, 0xc0 }, /* Index 1: 433 MHz 
(corrected) */
+  { 0x03, 0x0f, 0x1e, 0x27, 0x67, 0x50, 0x81, 0xc2 }, /* Index 2: 868 MHz */
+  { 0x03, 0x0e, 0x1e, 0x27, 0x39, 0x8e, 0xcd, 0xc0 }, /* Index 3: 915 MHz */
+};
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
@@ -1171,6 +1194,19 @@ int cc1101_deinit(FAR struct cc1101_dev_s *dev)
 int cc1101_powerup(FAR struct cc1101_dev_s *dev)
 {
   DEBUGASSERT(dev);
+  /* TODO: Execute underlying CSn pull-down wake sequence (if hardware
+   * requires it).
+   */
+
+  /* Core fix: Must rebuild volatile PATABLE memory and ramp curve after
+   * wake-up.
+   */
+
+  if (dev->status != CC1101_INIT)
+    {
+      cc1101_ioctl_apply_power(dev, dev->power);
+    }
+
   return 0;
 }
 
@@ -1357,6 +1393,95 @@ uint8_t cc1101_setpower(FAR struct cc1101_dev_s *dev, 
uint8_t power)
   return dev->power;
 }
 
+/****************************************************************************
+ * Name: cc1101_ioctl_apply_power
+ *
+ * Description:
+ * Real-time generation of the PATABLE curve based on physical hardware
+ * state (FREQ2 for band matching, MDMCFG2 for modulation strategy).
+ ****************************************************************************/
+
+static int cc1101_ioctl_apply_power(FAR struct cc1101_dev_s *dev,
+                                    uint8_t pwr_idx)
+{
+  uint8_t freq2;
+  uint8_t mdmcfg2;
+  uint8_t frend0;
+  uint8_t band_idx;
+  uint8_t mod_format;
+  uint8_t patable[8];
+  int i;
+  int ret;
+
+  /* Fact detection 1: Read FREQ2 to get real band to select
+   * calibration table.
+   */
+
+  if (cc1101_access(dev, CC1101_FREQ2, &freq2, 1) < 0)
+    {
+      return -EIO;
+    }
+
+  if (freq2 < 0x10)       band_idx = 0; /* 315 MHz */
+  else if (freq2 < 0x20)  band_idx = 1; /* 433 MHz */
+  else if (freq2 < 0x22)  band_idx = 2; /* 868 MHz */
+  else                    band_idx = 3; /* 915 MHz */
+
+  /* Fact detection 2: Read modulation format to decide whether to
+   * keep ramp-up curve.
+   */
+
+  if (cc1101_access(dev, CC1101_MDMCFG2, &mdmcfg2, 1) < 0)
+    {
+      return -EIO;
+    }
+
+  mod_format = (mdmcfg2 & 0x70) >> 4;
+
+  /* Dynamically generate physical PATABLE memory structure */
+
+  if (mod_format == 0x03) /* ASK / OOK */
+    {
+      /* Logic 0 must be silent (0x00), logic 1 is target output power */
+
+      patable[0] = 0x00;
+      patable[1] = g_cc1101_pa_calibration[band_idx][pwr_idx];
+
+      for (i = 2; i < 8; i++) patable[i] = 0x00; /* Eliminate memory residue */
+
+      /* Apply 8-byte burst write to cover the entire PATABLE space */
+
+      ret = cc1101_access(dev, CC1101_PATABLE, patable, -8);
+    }
+  else /* FSK series (including MSK / 4-FSK) */
+    {
+      /* Automatically synthesize smooth ramp-up curve using preset hex
+       * power values.
+       */
+
+      for (i = 0; i < 8; i++)
+        {
+          if (i <= pwr_idx)
+            patable[i] = g_cc1101_pa_calibration[band_idx][i];
+          else
+            patable[i] = g_cc1101_pa_calibration[band_idx][pwr_idx]; /* Stay 
flat (Plateau) after reaching target power */
+        }
+
+      ret = cc1101_access(dev, CC1101_PATABLE, patable, -8);
+    }
+
+  if (ret < 0) return -EIO;
+
+  /* Update FREND0.PA_POWER index, but must keep TX filter high bits */
+
+  if (cc1101_access(dev, CC1101_FREND0, &frend0, 1) < 0) return -EIO;
+
+  frend0 &= 0xf8; /* Clear bottom 3 control bits */
+  frend0 |= (mod_format == 0x03) ? 1 : 7;
+
+  return cc1101_access(dev, CC1101_FREND0, &frend0, -1);
+}
+
 /****************************************************************************
  * Name: cc1101_calc_rssi_dbm
  *
@@ -1724,22 +1849,31 @@ static int cc1101_file_ioctl(FAR struct file *filep, 
int cmd,
           uint8_t regs[3];
           uint8_t channr;
 
-          DEBUGASSERT(ptr32 != NULL);
+          if (ptr32 == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
 
           freq_word = ((uint64_t)(*ptr32) << 16) / f_xosc;
           regs[0] = (uint8_t)((freq_word >> 16) & 0xff);
           regs[1] = (uint8_t)((freq_word >> 8) & 0xff);
           regs[2] = (uint8_t)(freq_word & 0xff);
 
-          ret = cc1101_access(dev, CC1101_FREQ2, regs, -3);
-          if (ret >= 0)
+          if (cc1101_access(dev, CC1101_FREQ2, regs, -3) >= 0)
             {
-              channr = 0;
-              ret = cc1101_access(dev, CC1101_CHANNR, &channr, -1);
-              if (ret >= 0)
-                {
-                  ret = OK;
-                }
+              channr = 0; /* Clear logical channel bias to ensure absolute 
frequency accuracy */
+              cc1101_access(dev, CC1101_CHANNR, &channr, -1);
+
+              /* Frequency changed, immediately rebuild power table based
+               * on current band and target index.
+               */
+
+              ret = cc1101_ioctl_apply_power(dev, dev->power);
+            }
+          else
+            {
+              ret = -EIO;
             }
         }
         break;
@@ -1749,7 +1883,11 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
           uint8_t regs[3];
           uint32_t freq_word;
 
-          DEBUGASSERT(ptr32 != NULL);
+          if (ptr32 == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
 
           ret = cc1101_access(dev, CC1101_FREQ2, regs, 3);
           if (ret >= 0)
@@ -1765,7 +1903,11 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
 
       case WLIOC_SETADDR:
         {
-          DEBUGASSERT(ptr8 != NULL);
+          if (ptr8 == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
 
           ret = cc1101_access(dev, CC1101_ADDR, ptr8, -1);
           if (ret >= 0)
@@ -1777,7 +1919,11 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
 
       case WLIOC_GETADDR:
         {
-          DEBUGASSERT(ptr8 != NULL);
+          if (ptr8 == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
 
           ret = cc1101_access(dev, CC1101_ADDR, ptr8, 1);
           if (ret >= 0)
@@ -1791,18 +1937,62 @@ static int cc1101_file_ioctl(FAR struct file *filep, 
int cmd,
 
       case WLIOC_SETTXPOWER:
         {
-          DEBUGASSERT(ptr32_s != NULL);
+          int32_t req_dbm;
+          int32_t min_diff = INT32_MAX;
+          int32_t diff;
+          uint8_t best_idx = 0;
+          int i;
 
-          cc1101_setpower(dev, (uint8_t)*ptr32_s);
-          ret = OK;
+          if (ptr32_s == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
+
+          req_dbm = *ptr32_s;
+
+          /* Quantized nearest matching algorithm */
+
+          for (i = 0; i < 8; i++)
+            {
+              diff = req_dbm - g_cc1101_dbm_table[i];
+              if (diff < 0) diff = -diff;
+
+              if (diff < min_diff)
+                {
+                  min_diff = diff;
+                  best_idx = i;
+                }
+            }
+
+          /* Write the new absolute index into device state */
+
+          dev->power = best_idx;
+          ret = cc1101_ioctl_apply_power(dev, dev->power);
         }
         break;
 
       case WLIOC_GETTXPOWER:
         {
-          DEBUGASSERT(ptr32_s != NULL);
+          if (ptr32_s == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
+
+          /* Safely read physical dBm scale from newly defined state
+           * machine.
+           */
+
+          if (dev->power <= 7)
+            {
+              *ptr32_s = g_cc1101_dbm_table[dev->power];
+            }
+          else
+            {
+              *ptr32_s = g_cc1101_dbm_table[7];
+            }
 
-          *ptr32_s = (int32_t)dev->power;
           ret = OK;
         }
         break;
@@ -1812,36 +2002,110 @@ static int cc1101_file_ioctl(FAR struct file *filep, 
int cmd,
       case WLIOC_SETMODU:
         {
           uint8_t mdmcfg2;
+          uint8_t mdmcfg4;
+          uint8_t mdmcfg3;
+          uint32_t current_baud;
+          uint8_t e;
+          uint8_t m;
 
-          DEBUGASSERT(mod != NULL);
+          if (mod == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
 
-          ret = cc1101_access(dev, CC1101_MDMCFG2, &mdmcfg2, 1);
-          if (ret >= 0)
+          /* Read the current modulation configuration */
+
+          if (cc1101_access(dev, CC1101_MDMCFG2, &mdmcfg2, 1) < 0)
+            {
+              ret = -EIO;
+              break;
+            }
+
+          /* Hardware constraint 1: MSK modulation is only supported
+           * when data rate > 26 kBaud.
+           */
+
+          if (*mod == WLIOC_MSK)
             {
-              mdmcfg2 &= ~0x70;
-              switch (*mod)
+              if (cc1101_access(dev, CC1101_MDMCFG4, &mdmcfg4, 1) >= 0 &&
+                  cc1101_access(dev, CC1101_MDMCFG3, &mdmcfg3, 1) >= 0)
                 {
-                  case WLIOC_FSK:
-                    mdmcfg2 |= 0x00;
-                    break;
-                  case WLIOC_GFSK:
-                    mdmcfg2 |= 0x10;
-                    break;
-                  case WLIOC_OOK:
-                    mdmcfg2 |= 0x30;
-                    break;
-                  default:
-                    ret = -ENOTSUP;
-                    break;
+                  e = mdmcfg4 & 0x0f;
+                  m = mdmcfg3;
+
+                  /* Use 64-bit unsigned integer to prevent shift
+                   * overflow.
+                   */
+
+                  current_baud = (uint32_t)((((uint64_t)(256 + m) << e) *
+                                             f_xosc) >> 28);
+
+                  if (current_baud <= 26000)
+                    {
+                      /* Baud rate non-compliant, refuse to switch
+                       * modulation format.
+                       */
+
+                      ret = -EINVAL;
+                      break;
+                    }
+                }
+              else
+                {
+                  ret = -EIO;
+                  break;
                 }
+            }
+
+          /* Clear existing MOD_FORMAT flag bits (bits 6:4) */
+
+          mdmcfg2 &= ~0x70;
+
+          /* Hardware constraint 2: Manchester encoding is incompatible
+           * with MSK and 4-FSK, force clear MANCHESTER_EN (bit 3).
+           */
+
+          if (*mod == WLIOC_MSK || *mod == WLIOC_4FSK)
+            {
+              mdmcfg2 &= ~0x08;
+            }
 
+          /* Apply new modulation format */
+
+          switch (*mod)
+            {
+              case WLIOC_FSK:
+                mdmcfg2 |= 0x00;
+                break;
+              case WLIOC_GFSK:
+                mdmcfg2 |= 0x10;
+                break;
+              case WLIOC_OOK:
+                mdmcfg2 |= 0x30;
+                break;
+              case WLIOC_4FSK:
+                mdmcfg2 |= 0x40; /* 100: 4-FSK */
+                break;
+              case WLIOC_MSK:
+                mdmcfg2 |= 0x70; /* 111: MSK */
+                break;
+              default:
+                ret = -ENOTSUP;
+                break;
+            }
+
+          if (ret >= 0)
+            {
+              ret = cc1101_access(dev, CC1101_MDMCFG2, &mdmcfg2, -1);
               if (ret >= 0)
                 {
-                  ret = cc1101_access(dev, CC1101_MDMCFG2, &mdmcfg2, -1);
-                  if (ret >= 0)
-                    {
-                      ret = OK;
-                    }
+                  /* Modulation format changed (e.g. from FSK to OOK)
+                   * Must immediately rearrange PATABLE 0x00 mapping or
+                   * ramping to prevent bus deadlock.
+                   */
+
+                  ret = cc1101_ioctl_apply_power(dev, dev->power);
                 }
             }
         }
@@ -1852,7 +2116,11 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
           uint8_t mdmcfg2;
           uint8_t mod_format;
 
-          DEBUGASSERT(mod != NULL);
+          if (mod == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
 
           ret = cc1101_access(dev, CC1101_MDMCFG2, &mdmcfg2, 1);
           if (ret >= 0)
@@ -1861,7 +2129,6 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
               switch (mod_format)
                 {
                   case 0x00:
-                  case 0x04:
                     *mod = WLIOC_FSK;
                     break;
                   case 0x01:
@@ -1870,6 +2137,12 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
                   case 0x03:
                     *mod = WLIOC_OOK;
                     break;
+                  case 0x04:
+                    *mod = WLIOC_4FSK;
+                    break;
+                  case 0x07:
+                    *mod = WLIOC_MSK;
+                    break;
                   default:
                     ret = -ENOTSUP;
                     break;
@@ -1893,7 +2166,11 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
           uint8_t m;
           uint8_t mdmcfg4;
 
-          DEBUGASSERT(ptr32 != NULL);
+          if (ptr32 == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
 
           w = ((uint64_t)(*ptr32) << 28) / f_xosc;
           while (w > 511 && e < 15)
@@ -1902,9 +2179,15 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
               e++;
             }
 
-          if (w < 256)
+          /* Anti-overflow clamping logic (Clamping) */
+
+          if (w > 511)
+            {
+              w = 511; /* Limit to maximum possible mantissa */
+            }
+          else if (w < 256)
             {
-              w = 256;
+              w = 256; /* Limit to minimum possible mantissa */
             }
 
           m = (uint8_t)(w - 256);
@@ -1934,7 +2217,11 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
           uint8_t e;
           uint8_t m;
 
-          DEBUGASSERT(ptr32 != NULL);
+          if (ptr32 == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
 
           if (cc1101_access(dev, CC1101_MDMCFG4, &mdmcfg4, 1) >= 0 &&
               cc1101_access(dev, CC1101_MDMCFG3, &mdmcfg3, 1) >= 0)
@@ -1961,7 +2248,11 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
           uint8_t m;
           uint8_t deviatn;
 
-          DEBUGASSERT(ptr32 != NULL);
+          if (ptr32 == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
 
           w = ((uint64_t)(*ptr32) << 17) / f_xosc;
           while (w > 15 && e < 7)
@@ -1970,7 +2261,13 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
               e++;
             }
 
-          if (w < 8)
+          /* Anti-overflow clamping logic (Clamping) */
+
+          if (w > 15)
+            {
+              w = 15;
+            }
+          else if (w < 8)
             {
               w = 8;
             }
@@ -1992,7 +2289,11 @@ static int cc1101_file_ioctl(FAR struct file *filep, int 
cmd,
           uint8_t e;
           uint8_t m;
 
-          DEBUGASSERT(ptr32 != NULL);
+          if (ptr32 == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
 
           ret = cc1101_access(dev, CC1101_DEVIATN, &deviatn, 1);
           if (ret >= 0)
@@ -2012,12 +2313,22 @@ static int cc1101_file_ioctl(FAR struct file *filep, 
int cmd,
 
       case CC1101IOC_SETOPMODE:
         {
-          enum cc1101_opmode_e new_mode = (enum cc1101_opmode_e)arg;
+          FAR enum cc1101_opmode_e *mode_ptr =
+            (FAR enum cc1101_opmode_e *)((uintptr_t)arg);
+          enum cc1101_opmode_e new_mode;
           uint8_t pktctrl0;
           uint8_t pktctrl1;
           uint8_t iocfg0;
           uint8_t iocfg2;
 
+          if (mode_ptr == NULL)
+            {
+              ret = -EFAULT;
+              goto ioctl_out;
+            }
+
+          new_mode = *mode_ptr;
+
           /* 1. Restore baseline settings (read from rfsettings). */
 
           /* Clear PKT_FORMAT bits. */
@@ -2105,7 +2416,16 @@ ioctl_out:
 
       case CC1101IOC_GETOPMODE:
         {
-          FAR *(enum cc1101_opmode_e *)arg = dev->opmode;
+          FAR enum cc1101_opmode_e *mode_ptr =
+            (FAR enum cc1101_opmode_e *)((uintptr_t)arg);
+
+          if (mode_ptr == NULL)
+            {
+              ret = -EFAULT;
+              break;
+            }
+
+          *mode_ptr = dev->opmode;
           ret = OK;
           break;
         }
diff --git a/include/nuttx/wireless/ioctl.h b/include/nuttx/wireless/ioctl.h
index 12b2fdfdfe4..f67c2e18b8e 100644
--- a/include/nuttx/wireless/ioctl.h
+++ b/include/nuttx/wireless/ioctl.h
@@ -300,7 +300,9 @@ enum wlioc_modulation_e
   WLIOC_LORA,
   WLIOC_FSK,
   WLIOC_GFSK,
-  WLIOC_OOK
+  WLIOC_OOK,
+  WLIOC_4FSK,
+  WLIOC_MSK
 };
 
 /* LoRa common types ********************************************************/

Reply via email to