luat_lib_pm.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /*
  2. @module pm
  3. @summary 电源管理
  4. @version 1.0
  5. @date 2020.07.02
  6. @demo pm
  7. @tag LUAT_USE_PM
  8. @usage
  9. --[[
  10. 休眠模式简介
  11. -- IDLE 正常运行模式
  12. -- LIGHT 轻睡眠模式:
  13. CPU暂停
  14. RAM保持供电
  15. 定时器/网络事件/IO中断均可自动唤醒
  16. 唤醒后程序继续运行
  17. 普通GPIO掉电,外设驱动掉电
  18. AON_GPIO保持电平
  19. -- DEEP 深睡眠模式
  20. CPU暂停
  21. 核心RAM掉电, 保留RAM维持供电
  22. 普通GPIO掉电,外设驱动掉电
  23. AON_GPIO保持休眠前的电平
  24. dtimer定时器可唤醒
  25. wakeup脚可唤醒
  26. 唤醒后程序从头运行,休眠前的运行时数据全丢
  27. -- HIB 休眠模式
  28. CPU暂停
  29. RAM掉电, 保留RAM也掉电
  30. 普通GPIO掉电,外设驱动掉电
  31. AON_GPIO保持休眠前的电平
  32. dtimer定时器可唤醒
  33. wakeup脚可唤醒
  34. 唤醒后程序从头运行,休眠前的运行时数据全丢
  35. 对部分模块,例如Air780EXXX, DEEP/HIB对用户代码没有区别
  36. 除pm.shutdown()外, RTC总是运行的, 除非掉电
  37. ]]
  38. -- 定时器唤醒, 请使用 pm.dtimerStart()
  39. -- wakeup唤醒
  40. -- 如Air101/Air103, 有独立的wakeup脚, 不需要配置,可直接控制唤醒
  41. -- 如Air780EXXX系列, 有多个wakeup可用, 通过gpio.setup()配置虚拟GPIO进行唤醒配置,参考demo/gpio/virtualIO
  42. pm.request(pm.IDLE) -- 通过切换不同的值请求进入不同的休眠模式
  43. -- 对应Air780EXXX系列, 执行后并不一定马上进入休眠模式, 如无后续数据传输需求,可先进入飞行模式,然后快速休眠
  44. */
  45. #include "lua.h"
  46. #include "lauxlib.h"
  47. #include "luat_base.h"
  48. #include "luat_pm.h"
  49. #include "luat_msgbus.h"
  50. #define LUAT_LOG_TAG "pm"
  51. #include "luat_log.h"
  52. #ifdef LUAT_USE_DRV_PM
  53. #include "luat_airlink.h"
  54. #include "luat/drv_pm.h"
  55. extern uint32_t g_airlink_pause;
  56. #endif
  57. // static int lua_event_cb = 0;
  58. /*
  59. @sys_pub pm
  60. deep sleep timer定时时间到回调
  61. DTIMER_WAKEUP
  62. @usage
  63. sys.subscribe("DTIMER_WAKEUP", function(timer_id)
  64. log.info("deep sleep timer", timer_id)
  65. end)
  66. */
  67. int luat_dtimer_cb(lua_State *L, void* ptr) {
  68. rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
  69. lua_getglobal(L, "sys_pub");
  70. if (lua_isfunction(L, -1)) {
  71. lua_pushstring(L, "DTIMER_WAKEUP");
  72. lua_pushinteger(L, msg->arg1);
  73. lua_call(L, 2, 0);
  74. }
  75. return 0;
  76. }
  77. /**
  78. 请求进入指定的休眠模式
  79. @api pm.request(mode, chip)
  80. @int 休眠模式,例如pm.IDLE/LIGHT/DEEP/HIB.
  81. @int 休眠芯片的ID, 默认是0, 大部分型号都只有0
  82. @return boolean 处理结果,即使返回成功,也不一定会进入, 也不会马上进入
  83. @usage
  84. -- 请求进入休眠模式
  85. --[[
  86. IDLE 正常运行,就是无休眠
  87. LIGHT 轻休眠, CPU停止, RAM保持, 外设保持, 可中断唤醒. 部分型号支持从休眠处继续运行
  88. DEEP 深休眠, CPU停止, RAM掉电, 仅特殊引脚保持的休眠前的电平, 大部分管脚不能唤醒设备.
  89. HIB 彻底休眠, CPU停止, RAM掉电, 仅复位/特殊唤醒管脚可唤醒设备.
  90. ]]
  91. pm.request(pm.HIB)
  92. */
  93. static int l_pm_request(lua_State *L) {
  94. int mode = luaL_checkinteger(L, 1);
  95. int ret = 0;
  96. #ifdef LUAT_USE_DRV_PM
  97. int chip = 0;
  98. if (lua_isinteger(L, 2)) {
  99. chip = luaL_checkinteger(L, 2);
  100. if (chip > 0) {
  101. g_airlink_pause = 1; // wifi进入休眠自动暂停airlink工作
  102. }
  103. }
  104. ret = luat_drv_pm_request(chip, mode);
  105. #else
  106. ret = luat_pm_request(mode);
  107. #endif
  108. lua_pushboolean(L, ret == 0 ? 1 : 0);
  109. return 1;
  110. }
  111. /**
  112. 启动底层定时器,在休眠模式下依然生效. 只触发一次,关机状态下无效
  113. @api pm.dtimerStart(id, timeout)
  114. @int 定时器id,通常是0-5
  115. @int 定时时长,单位毫秒
  116. @return boolean 处理结果
  117. @usage
  118. -- 添加底层定时器
  119. pm.dtimerStart(0, 300 * 1000) -- 5分钟后唤醒
  120. -- 针对Air780EXXX有如下限制
  121. -- id = 0 或者 id = 1 是, 最大休眠时长是2.5小时
  122. -- id >= 2是, 最大休眠时长是740小时
  123. */
  124. static int l_pm_dtimer_start(lua_State *L) {
  125. int dtimer_id = luaL_checkinteger(L, 1);
  126. int timeout = luaL_checkinteger(L, 2);
  127. if (luat_pm_dtimer_start(dtimer_id, timeout)) {
  128. lua_pushboolean(L, 0);
  129. }
  130. else {
  131. lua_pushboolean(L, 1);
  132. }
  133. return 1;
  134. }
  135. /**
  136. 关闭底层定时器
  137. @api pm.dtimerStop(id)
  138. @int 定时器id
  139. @usage
  140. -- 关闭底层定时器
  141. pm.dtimerStop(0) -- 关闭id=0的底层定时器
  142. */
  143. static int l_pm_dtimer_stop(lua_State *L) {
  144. int dtimer_id = luaL_checkinteger(L, 1);
  145. luat_pm_dtimer_stop(dtimer_id);
  146. return 0;
  147. }
  148. #ifndef LUAT_COMPILER_NOWEAK
  149. LUAT_WEAK uint32_t luat_pm_dtimer_remain(int id){
  150. return -1;
  151. }
  152. LUAT_WEAK int luat_pm_get_last_req_mode(void){
  153. return -1;
  154. }
  155. #endif
  156. /**
  157. 检查底层定时器是不是在运行
  158. @api pm.dtimerCheck(id)
  159. @int 定时器id
  160. @return boolean 处理结果,true还在运行,false不在运行
  161. @return number 如果运行,运行剩余时间,单位毫秒(需bsp支持)
  162. @usage
  163. -- 检查底层定时器是不是在运行
  164. pm.dtimerCheck(0) -- 检查id=0的底层定时器
  165. */
  166. static int l_pm_dtimer_check(lua_State *L) {
  167. int dtimer_id = luaL_checkinteger(L, 1);
  168. if (luat_pm_dtimer_check(dtimer_id)){
  169. uint32_t remain = luat_pm_dtimer_remain(dtimer_id);
  170. lua_pushboolean(L, 1);
  171. lua_pushinteger(L, remain);
  172. return 2;
  173. }else{
  174. lua_pushboolean(L, 0);
  175. return 1;
  176. }
  177. }
  178. static int l_pm_dtimer_list(lua_State *L) {
  179. size_t c = 0;
  180. size_t dlist[24];
  181. luat_pm_dtimer_list(&c, dlist);
  182. lua_createtable(L, c, 0);
  183. for (size_t i = 0; i < c; i++)
  184. {
  185. if (dlist[i] > 0) {
  186. lua_pushinteger(L, dlist[i]);
  187. lua_seti(L, -3, i+1);
  188. }
  189. }
  190. return 1;
  191. }
  192. /**
  193. 检查定时唤醒是哪一个定时器,如果不是定时唤醒的,返回-1
  194. @api pm.dtimerWkId()
  195. @return int 处理结果 >=0 是本次定时唤醒的定时器ID,其他错误,说明不是定时唤醒的
  196. @usage
  197. local timer_id = pm.dtimerWkId()
  198. */
  199. static int l_pm_dtimer_wakeup_id(lua_State *L) {
  200. int dtimer_id = 0xFF;
  201. luat_pm_dtimer_wakeup_id(&dtimer_id);
  202. if (dtimer_id != 0xFF) {
  203. lua_pushinteger(L, dtimer_id);
  204. }
  205. else {
  206. lua_pushinteger(L, -1);
  207. }
  208. return 1;
  209. }
  210. /**
  211. 开机原因,用于判断是从休眠模块开机,还是电源/复位开机
  212. @api pm.lastReson()
  213. @return int 0-上电/复位开机, 1-RTC开机, 2-WakeupIn/Pad/IO开机, 3-未知原因(Wakeup/RTC皆有可能)开机,目前只有air101,air103会有这个返回值
  214. @return int 0-普通开机(上电/复位),3-深睡眠开机,4-休眠开机
  215. @return int 复位开机详细原因:0-powerkey或者上电开机 1-充电或者AT指令下载完成后开机 2-闹钟开机 3-软件重启 4-未知原因 5-RESET键 6-异常重启 7-工具控制重启 8-内部看门狗重启 9-外部重启 10-充电开机
  216. @usage
  217. -- 是哪种方式开机呢
  218. log.info("pm", "last power reson", pm.lastReson())
  219. */
  220. static int l_pm_last_reson(lua_State *L) {
  221. int lastState = 0;
  222. int rtcOrPad = 0;
  223. luat_pm_last_state(&lastState, &rtcOrPad);
  224. lua_pushinteger(L, rtcOrPad);
  225. lua_pushinteger(L, lastState);
  226. lua_pushinteger(L, luat_pm_get_poweron_reason());
  227. return 3;
  228. }
  229. /**
  230. 强制进入指定的休眠模式,忽略某些外设的影响,比如USB
  231. @api pm.force(mode)
  232. @int 休眠模式
  233. @return boolean 处理结果,若返回成功,大概率会马上进入该休眠模式
  234. @usage
  235. -- 请求进入休眠模式
  236. pm.force(pm.HIB)
  237. -- 针对Air780EXXX, 该操作会关闭USB通信
  238. -- 唤醒后如需开启USB, 请打开USB电压
  239. --pm.power(pm.USB, true)
  240. */
  241. static int l_pm_force(lua_State *L) {
  242. lua_pushinteger(L, luat_pm_force(luaL_checkinteger(L, 1)));
  243. return 1;
  244. }
  245. /**
  246. 检查休眠状态
  247. @api pm.check()
  248. @return boolean 处理结果,如果能顺利进入休眠,返回true,否则返回false
  249. @return int 底层返回值,0代表能进入最底层休眠,其他值代表最低可休眠级别
  250. @usage
  251. -- 请求进入休眠模式,然后检查是否能真的休眠
  252. pm.request(pm.HIB)
  253. if pm.check() then
  254. log.info("pm", "it is ok to hib")
  255. else
  256. -- 针对Air780EXXX, 该操作会关闭USB通信
  257. pm.force(pm.HIB) -- 强制休眠
  258. -- 唤醒后如需开启USB, 请打开USB电压
  259. --sys.wait(100)
  260. --pm.power(pm.USB, true)
  261. end
  262. */
  263. static int l_pm_check(lua_State *L) {
  264. int ret = luat_pm_check();
  265. lua_pushboolean(L, luat_pm_check() == 0 ? 1 : 0);
  266. lua_pushinteger(L, ret);
  267. return 2;
  268. }
  269. #ifndef LUAT_COMPILER_NOWEAK
  270. LUAT_WEAK int luat_pm_poweroff(void) {
  271. LLOGW("powerOff is not supported");
  272. return -1;
  273. }
  274. #else
  275. extern int luat_pm_poweroff(void);
  276. #endif
  277. /**
  278. 关机
  279. @api pm.shutdown()
  280. @return nil 无返回值
  281. @usage
  282. -- 当前支持移芯CAT1平台系列(Air780E/Air700E/Air780EP等等)
  283. -- 需要2022-12-22之后编译的固件
  284. pm.shutdown()
  285. */
  286. static int l_pm_power_off(lua_State *L) {
  287. (void)L;
  288. luat_pm_poweroff();
  289. return 0;
  290. }
  291. /**
  292. 重启
  293. @api pm.reboot()
  294. @return nil 无返回值
  295. -- 立即重启设备, 本函数的行为与rtos.reboot()完全一致,只是在pm库做个别名
  296. pm.reboot()
  297. */
  298. int l_rtos_reboot(lua_State *L);
  299. int l_rtos_standby(lua_State *L);
  300. /**
  301. 开启内部的电源控制,注意不是所有的平台都支持,可能部分平台支持部分选项,看硬件
  302. @api pm.power(id, onoff, chip)
  303. @int 电源控制id,pm.USB pm.GPS之类
  304. @boolean/int 开关true/1开,false/0关,默认关,部分选项支持数值
  305. @int 休眠芯片的ID, 默认是0, 大部分型号都只有0
  306. @return boolean 处理结果true成功,false失败
  307. @usage
  308. -- 关闭USB电源, 反之开启就是传true
  309. pm.power(pm.USB, false)
  310. -- Air780EG,为内置的GPS芯片上电. 注意, Air780EG的GPS和GPS_ANT是一起控制的,所以合并了.
  311. pm.power(pm.GPS, true)
  312. -- Air780EXXX开启pwrkey开机防抖
  313. -- 注意: 开启后, 复位键就变成关机了!!! pwrkey要长按2秒才能开机
  314. -- pm.power(pm.PWK_MODE, true)
  315. -- Air780EXXX PSM+低功耗设置
  316. -- Air780EXXX节能模式,0~3,0完全关闭,1~2普通低功耗,3超低功耗,深度休眠
  317. -- 详情访问: https://airpsm.cn
  318. -- pm.power(pm.WORK_MODE, 1)
  319. */
  320. static int l_pm_power_ctrl(lua_State *L) {
  321. uint8_t onoff = 0;
  322. int id = luaL_checkinteger(L, 1);
  323. int ret = 0;
  324. if (lua_isboolean(L, 2)) {
  325. onoff = lua_toboolean(L, 2);
  326. }
  327. else
  328. {
  329. onoff = lua_tointeger(L, 2);
  330. }
  331. #ifdef LUAT_USE_DRV_PM
  332. int chip = 0;
  333. if (lua_isinteger(L, 3)) {
  334. chip = luaL_checkinteger(L, 3);
  335. }
  336. ret = luat_drv_pm_power_ctrl(chip, id, onoff);
  337. #else
  338. ret = luat_pm_power_ctrl(id, onoff);
  339. #endif
  340. lua_pushboolean(L, !ret);
  341. return 1;
  342. }
  343. /**
  344. IO高电平和对外输出LDO的电压控制
  345. @api pm.ioVol(id, val)
  346. @int 电平id,目前只有pm.IOVOL_ALL_GPIO
  347. @int 电平值,单位毫伏
  348. @return boolean 处理结果true成功,false失败
  349. @usage
  350. -- Air780EXXX设置IO电平, 范围 1650 ~ 2000,2650~3400 , 单位毫伏, 步进50mv
  351. -- 注意, 这里的设置优先级会高于硬件IOSEL脚的配置
  352. -- 但开机时依然先使用硬件配置,直至调用本API进行配置, 所以io电平会变化
  353. -- pm.ioVol(pm.IOVOL_ALL_GPIO, 3300) -- 所有GPIO高电平输出3.3V
  354. -- pm.ioVol(pm.IOVOL_ALL_GPIO, 1800) -- 所有GPIO高电平输出1.8V
  355. */
  356. static int l_pm_iovolt_ctrl(lua_State *L) {
  357. int val = 3300;
  358. int id = luaL_optinteger(L, 1, LUAT_PM_ALL_GPIO);
  359. if (lua_isboolean(L, 2)) {
  360. val = lua_toboolean(L, 2);
  361. }
  362. else if (lua_isinteger(L, 2)) {
  363. val = luaL_checkinteger(L, 2);
  364. }
  365. lua_pushboolean(L, !luat_pm_iovolt_ctrl(id, val));
  366. return 1;
  367. }
  368. #ifndef LUAT_COMPILER_NOWEAK
  369. LUAT_WEAK int luat_pm_iovolt_ctrl(int id, int val) {
  370. return -1;
  371. }
  372. #endif
  373. /**
  374. 配置唤醒引脚
  375. @api pm.wakeupPin(pin,level)
  376. @int gpio引脚
  377. @int 唤醒方式, 例如gpio.RISING (上升沿), gpio.FALLING (下降沿)
  378. @int 芯片的ID, 默认是0, 大部分型号都只有0
  379. @return boolean 处理结果
  380. @usage
  381. pm.wakeupPin(8, gpio.RISING)
  382. */
  383. static int l_pm_wakeup_pin(lua_State *L) {
  384. int level = luaL_optinteger(L, 2,0);
  385. if (lua_istable(L, 1)) {
  386. size_t count = lua_rawlen(L, 1);
  387. for (size_t i = 1; i <= count; i++){
  388. lua_geti(L, 1, i);
  389. luat_pm_wakeup_pin(luaL_checkinteger(L, -1), level);
  390. lua_pop(L, 1);
  391. }
  392. }else if(lua_isnumber(L, 1)){
  393. #ifdef LUAT_USE_DRV_PM
  394. int chip = 0;
  395. if (lua_isinteger(L, 3)) {
  396. chip = luaL_checkinteger(L, 3);
  397. }
  398. luat_drv_pm_wakeup_pin(chip, luaL_checkinteger(L, 1), level);
  399. #else
  400. luat_pm_wakeup_pin(luaL_checkinteger(L, 1), level);
  401. #endif
  402. }
  403. lua_pushboolean(L, 1);
  404. return 1;
  405. }
  406. #include "rotable2.h"
  407. static const rotable_Reg_t reg_pm[] =
  408. {
  409. { "request" , ROREG_FUNC(l_pm_request )},
  410. // { "release" , ROREG_FUNC( l_pm_release)},
  411. { "dtimerStart", ROREG_FUNC(l_pm_dtimer_start)},
  412. { "dtimerStop" , ROREG_FUNC(l_pm_dtimer_stop)},
  413. { "dtimerCheck" , ROREG_FUNC(l_pm_dtimer_check)},
  414. { "dtimerList", ROREG_FUNC(l_pm_dtimer_list)},
  415. { "dtimerWkId", ROREG_FUNC(l_pm_dtimer_wakeup_id)},
  416. //{ "on", ROREG_FUNC(l_pm_on)},
  417. { "force", ROREG_FUNC(l_pm_force)},
  418. { "check", ROREG_FUNC(l_pm_check)},
  419. { "lastReson", ROREG_FUNC(l_pm_last_reson)},
  420. { "shutdown", ROREG_FUNC(l_pm_power_off)},
  421. { "reboot", ROREG_FUNC(l_rtos_reboot)},
  422. { "power", ROREG_FUNC(l_pm_power_ctrl)},
  423. { "ioVol", ROREG_FUNC(l_pm_iovolt_ctrl)},
  424. { "wakeupPin", ROREG_FUNC(l_pm_wakeup_pin)},
  425. //@const NONE number 不休眠模式
  426. { "NONE", ROREG_INT(LUAT_PM_SLEEP_MODE_NONE)},
  427. //@const IDLE number IDLE模式
  428. { "IDLE", ROREG_INT(LUAT_PM_SLEEP_MODE_IDLE)},
  429. //@const LIGHT number LIGHT模式
  430. { "LIGHT", ROREG_INT(LUAT_PM_SLEEP_MODE_LIGHT)},
  431. //@const DEEP number DEEP模式
  432. { "DEEP", ROREG_INT(LUAT_PM_SLEEP_MODE_DEEP)},
  433. //@const HIB number HIB模式
  434. { "HIB", ROREG_INT(LUAT_PM_SLEEP_MODE_STANDBY)},
  435. //@const USB number USB电源
  436. { "USB", ROREG_INT(LUAT_PM_POWER_USB)},
  437. //@const GPS number GPS电源
  438. { "GPS", ROREG_INT(LUAT_PM_POWER_GPS)},
  439. //@const GPS_ANT number GPS的天线电源,有源天线才需要
  440. { "GPS_ANT", ROREG_INT(LUAT_PM_POWER_GPS_ANT)},
  441. //@const CAMERA number camera电源,CAM_VCC输出
  442. { "CAMERA", ROREG_INT(LUAT_PM_POWER_CAMERA)},
  443. //@const DAC_EN number Air780EXXX的DAC_EN(新版硬件手册的LDO_CTL,同一个PIN,命名变更),注意audio的默认配置会自动使用这个脚来控制CODEC的使能
  444. { "DAC_EN", ROREG_INT(LUAT_PM_POWER_DAC_EN_PIN)},
  445. //@const LDO_CTL number Air780EXXX的LDO_CTL(老版硬件手册的DAC_EN,同一个PIN,命名变更),Air780EXXX的LDO_CTL, 注意audio的默认配置会自动使用这个脚来控制CODEC的使能
  446. { "LDO_CTL", ROREG_INT(LUAT_PM_POWER_LDO_CTL_PIN)},
  447. //@const PWK_MODE number 是否Air780EXXX的powerkey滤波模式,true开,注意滤波模式下reset变成直接关机
  448. { "PWK_MODE", ROREG_INT(LUAT_PM_POWER_POWERKEY_MODE)},
  449. //@const WORK_MODE number Air780EXXX的节能模式,0~3,0完全关闭,1~2普通低功耗,3超低功耗,深度休眠
  450. { "WORK_MODE", ROREG_INT(LUAT_PM_POWER_WORK_MODE)},
  451. //@const IOVOL_ALL_GPIO number 所有GPIO高电平电压控制,当前仅Air780EXXX可用
  452. { "IOVOL_ALL_GPIO", ROREG_INT(LUAT_PM_ALL_GPIO)},
  453. //@const IOVOL_SDIO number VMMC电压域IO
  454. { "IOVOL_SDIO", ROREG_INT(LUAT_PM_LDO_TYPE_VMMC)},
  455. //@const IOVOL_LCD number VLCD电压域IO
  456. { "IOVOL_LCD", ROREG_INT(LUAT_PM_LDO_TYPE_VLCD)},
  457. //@const ID_NATIVE number PM控制的ID, 主芯片, 任意芯片的默认值就是它
  458. { "ID_NATIVE", ROREG_INT(1)},
  459. //@const ID_WIFI number PM控制的ID, WIFI芯片, 仅Air8000可用
  460. { "ID_WIFI", ROREG_INT(1)},
  461. //@const WIFI_STA_DTIM number wifi芯片控制STA模式下的DTIM间隔,单位100ms,默认值是1
  462. { "WIFI_STA_DTIM", ROREG_INT(LUAT_PM_POWER_WIFI_STA_DTIM)},
  463. { "WIFI_AP_DTIM", ROREG_INT(LUAT_PM_POWER_WIFI_AP_DTIM)},
  464. { NULL, ROREG_INT(0) }
  465. };
  466. LUAMOD_API int luaopen_pm( lua_State *L ) {
  467. luat_newlib2(L, reg_pm);
  468. return 1;
  469. }