qnx str-ctl

str-ctl 实现源码

AMSS/platform/services/applications/str-ctl/main.c

int main ( int argc, char** argv)
{
    int c ;
    uint64_t entry_timeout_nsec = 1000000000ull;
    uint64_t wakeup_timeout_nsec = 1000000000ull;
    bool initiate = false ;

    while( ( c = getopt( argc, argv, "e:w:i" ) ) != -1 ) {
        switch( c ) {
            case 'e':
                entry_timeout_nsec = strtoull ( optarg, NULL, 0 );
                break;
            case 'w':
                wakeup_timeout_nsec = strtoull ( optarg, NULL, 0 );
                break;
            case 'i':
                initiate = true;
                break;
            case '?':
                ERR("Invalid option(%c), see \"use %s\"", c, argv[0]);
                return -1;
        }
    }

    if ( !initiate ) {
        ERR("'-i' option mandatory, see \"use %s\"", argv[0]);
        return EOK ;
    }

    int fd = open ( "/dev/pm", O_RDWR | O_CLOEXEC) ;
    if ( fd < 0 ) {
        ERR ("open(/dev/pm) failed, err=%d", errno);
        return -1;
    }

    struct timespec start, stop;
    uint64_t cycle, ncycles;
    uint64_t duration_nsec ;

    struct pm_str_s cmd = {0};
    cmd.entry_timeout = entry_timeout_nsec ;
    cmd.wakeup_timeout = wakeup_timeout_nsec ;

    (void) clock_gettime( CLOCK_REALTIME, &start) ;
    cycle = ClockCycles();

    int const rc = devctl ( fd, DCMD_PM_ENTER_STR , &cmd, sizeof (struct pm_str_s), 0 );

    ncycles = ClockCycles() - cycle;
    (void) clock_gettime( CLOCK_REALTIME, &stop) ;

    duration_nsec = timespec2nsec (&stop) - timespec2nsec (&start);

    if ( rc == EOK && duration_nsec < wakeup_timeout_nsec ) {
        LOG("STR interrupted, expected to sleep=%llu ms, actual sleep =%llu ms",
                NSEC_TO_MSEC ( wakeup_timeout_nsec ), NSEC_TO_MSEC (duration_nsec ));
    }

    LOG("DCMD_PM_ENTER_STR %s rc=%d",  rc ? "failed" : "succeeded" , rc );
    LOG("Elapsed system time -> %fs", 1.0f * duration_nsec / ( BILLION * 1.0f) );
    LOG("Elapsed raw cycles time -> %fs", (double) ncycles / SYSPAGE_ENTRY(qtime)->cycles_per_sec);

    close ( fd ) ;

    return rc ;
}

 str-ctl 触发整机休眠

qcom 参考文档
QNX Power Management Software  Architecture Reference Manual 80-PG469-7 Rev. N 
October 5, 2022 

3.6.2 Suspend QNX host Before the device can be put into LPM, 
terminate WFD and touch clients and turn off the  displays that are connected to the system by running the command. NOTE: The following commands to slay ‘calib-touch’ and ‘openwfd_telltale’ are just for test  purposes. In general, the expectation is that GFX/HMI related applications quiesced and  are aware of the various system power states such that they do not make any screen_*()  or openGL calls once the suspend to RAM sequence is kicked off by the OEM’s  lifecyclemanager. Contact QTI support for more information. 
 # slay calib-touch
 # slay openwfd_telltale 
 # screen-pwrmgr off 
 Turn off the USB controller that is managed by the host OS by running the command:
  # echo "BUS::stop,busno=0" >> /var/pps/device/usb_ctrl1 
  # echo "BUS::stop,busno=0" >> /var/pps/device/usb_ctrl2 
  The final step is to kick off the STR sequence by running the command: 
  # str-ctl -i -e 5000000000 -w 5000000000 
  A successful STR sequence looks like:
   # str-ctl -i -e 5000000000 -w 5000000000 
   [main:81] DCMD_PM_ENTER_STR succeeded rc=0 
   [main:82] Elapsed system time -> 5.284297s  
   [main:83] Elapsed raw cycles time -> 5.284475s

AMSS/platform/power/powermgr/src/pwrmgr.c

static int io_devctl (resmgr_context_t *ctp, io_devctl_t *msg,
        RESMGR_OCB_T  *_ocb)
{
    struct pm_ocb_s* ocb = (struct pm_ocb_s*)_ocb;
    struct pm_dev_s *dev = ocb->dev;
    int rc = iofunc_devctl_default(ctp, msg, (iofunc_ocb_t *)ocb);
    if (rc != _RESMGR_DEFAULT)
    {
        ERROR("devctl_default verify failed : %s",
                strerror(errno));
        return rc;
    }

    if (msg == NULL)
    {
        ERROR("Invalid parameter");
        return EINVAL;
    }

    const int dcmd = msg->i.dcmd;
    switch (dcmd) {
        case DCMD_PM_REGISTER:
            {
                return pm_register_client(ctp, msg, ocb);
            }
        case DCMD_PM_ACK:
            {
                return pm_handle_ack(ocb, msg, dev);
            }
        case DCMD_PM_ENTER_STR:
            {
                return pm_enter_str(ctp, msg, dev);
            }
        case DCMD_PM_RESUME:
            {
                return pm_resume_fsm ( dev, EOK ) ;
            }
        default:
            ERROR("Invalid DCMD(0x%x)", dcmd ) ;
            return ENOSYS;
    }
}
static int pm_enter_str(resmgr_context_t *ctp, io_devctl_t *msg, struct pm_dev_s *dev)
{
    int rc = EOK;
    if ( msg->i.nbytes != sizeof (struct pm_str_s)){
        INFO("Invalid size");
        return EINVAL;
    }

    if ( fsm_is_active (dev) ) {
        INFO("DCMD_PM_ENTER_STR already under progress");
        return EBUSY;
    }

    if ( ! dev->nclients ) {
        INFO("No registered clients");
        return ESRCH;
    }

    const struct pm_str_s *cmd = ( const struct pm_str_s *) _DEVCTL_DATA( msg->i );

    INFO("DCMD_PM_ENTER_STR start (entry_timeout=%lu ms, wakeup_timeout=%lu ms)",
            cmd->entry_timeout / ( 1000 * 1000) , cmd->wakeup_timeout / ( 1000 * 1000));

    rc = pm_settimer ( dev->timerid , cmd->entry_timeout ) ;
    if ( rc != EOK ) {
        ERROR("pm_settimer() failed rc=%d", rc);
        return rc ;
    }
    dev->str_time.start = ClockCycles();

    const enum fsm_result result = fsm_perform ( dev ) ;
    switch ( result ) {
        case FSM_IN_PROGRESS:
            break ;
        case FSM_TERMINATED:
            rc = fsm_get_initiator_rc ( dev );
            (void) fsm_conclude ( dev ) ;
            return rc ;
        default:
            (void) pm_settimer ( dev->timerid , 0);
            return EIO ;
    }

    pm_setwakeup ( dev, cmd->wakeup_timeout);
    fsm_set_initiator_rc  (dev, EOK ) ;
    fsm_set_initiator_rcvid  (dev, ctp->rcvid);

    return (_RESMGR_NOREPLY );
}

 单模块suspend resume

# Deliver prepare & suspend pulses to the audio driver by running  
# below commands 
# This is functionally similar to what the framework does during  
# full system suspend. 

echo prepare > /dev/pdbg/qcore/power/clients/audio_mcm.0 
echo suspend > /dev/pdbg/qcore/power/clients/audio_mcm.0 
# At this point if all active audio sessions are terminated then aDSP should power collapse itself. # Deliver resume and complete pulses by running below commands. 
# This is functionally similar to what the framework does during  
# full system resume. 
echo resume > /dev/pdbg/qcore/power/clients/audio_mcm.0 
echo complete > /dev/pdbg/qcore/power/clients/audio_mcm.0



#chmod 777 camera_server_pm.0
#camera suspend 
echo prepare > /dev/pdbg/qcore/power/clients/camera_server_pm.0 
echo suspend > /dev/pdbg/qcore/power/clients/camera_server_pm.0 
#camera resume
echo resume > /dev/pdbg/qcore/power/clients/camera_server_pm.0
echo complete > /dev/pdbg/qcore/power/clients/camera_server_pm.0

 //动态创建client

/dev/pdbg/qcore/power/clients/(client->name)

//提供动态模块

suspend resume 流程debug 节点

static int register_a_client ( struct pm_ocb_s *ocb, 
        const struct pm_register_s *req, pid_t pid ) 
{
    struct pm_dev_s *dev = ocb->dev;

    if (req->priority >= PM_PRIO_LEVEL_MAX) {
        return EINVAL;
    }

    if ( is_internal_level (req->priority)){
        return EINVAL;
    }

#define PM_VALID_FLAGS (PM_FLAG_NO_SUSPEND)
    if ( req->flags & (~PM_VALID_FLAGS) ){
        return EINVAL;
    }

    //Validate the pulse codes for collisions,
    //this should weed out any clients that do not 
    //use INIT_PM_REGISTER_STRUCT() to setup the 
    //register structure.
    for ( unsigned i = 0 ; i < PM_STATE_MAX ; i++ ) {
        if ( req->pulse_codes [i] < _PULSE_CODE_MINAVAIL || 
                req->pulse_codes [i] > _PULSE_CODE_MAXAVAIL ) 
            continue ;

        for ( unsigned j = i + 1 ; j < PM_STATE_MAX ; j ++ )  {
            if ( req->pulse_codes[i] == req->pulse_codes[j] )  {
                ERROR ("Pulse code collision of type \"%s(%d)\" with \"%s(%d)\"",
                        pm_state_to_str [i] , req->pulse_codes[i], 
                        pm_state_to_str [j] , req->pulse_codes[j] );
                return EINVAL;
            }
        }
    }

    struct pm_client_s *client = calloc ( 1 , sizeof (*client));
    if ( !client){
        return ENOMEM;
    }

    snprintf( client->name, sizeof (client->name) , "%s.%d", 
            req->name, get_num_clients_per_pid (dev, pid));

    client->pid = pid;

    for ( unsigned i = 0 ; i < PM_STATE_MAX ; i++ )  {
        client->pulse_codes[i] = req->pulse_codes[i] ;
    }

    pthread_mutex_init (&client->wait.mtx , NULL);
    pthread_condattr_t attr;
    pthread_condattr_init( &attr);
    pthread_condattr_setclock( &attr, CLOCK_MONOTONIC);
    pthread_cond_init( &client->wait.cond, &attr);

    client->prio_level = req->priority;
    client->flags = req->flags ; 
    client->coid = ConnectAttach(0, pid , req->chid , 
            _NTO_SIDE_CHANNEL,
            _NTO_COF_CLOEXEC);
    if ( client->coid == -1 ) {
        ERROR( "ConnectAttach(pid=%d,chid=%d) failed, err=%d", 
                pid, req->chid, errno ) ;
        free ( client ) ; 
        return EINVAL;
    }

    ocb->self = client ; 

    pthread_mutex_lock ( &dev->lock ) ;

    list_add_head ( &dev->client_heads[ req->priority ], &client->node  ) ;
    dev->nclients ++; 

    struct pdbg_ops_s pwr_client_votes = { 0 };
    pwr_client_votes.write2 = &pm_pdbg_ctl;
    char tmp[128]; 
    snprintf (tmp, sizeof(tmp) , "power/clients/%s", client->name);
    //动态创建 /dev/pdbg/qcore/power/clients/(client->name) 
    //提供动态模块suspend resume 流程debug 节点
    pdbg_create_file (tmp, 0220 , pwr_client_votes , (void * ) client );

    pthread_mutex_unlock ( &dev->lock ) ;

    INFO("Registered external client=\"%s\" at prio=%d", client->name, 
            req->priority ) ;

    return EOK ; 
}

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值