sja1000芯片can驱动程序

应用层使用socketCan的方法:http://pan.baidu.com/s/1ntsvbb7#path=%252Floongson1%252Ftools%252Fcan

功能:对can驱动程序的函数直接调用,而不经过设备驱动功能层、网络设备接口层、网络协议接口层

      像串口驱动程序调用can驱动程序函数时,首先要调用

  

mytscan1_probe(&m_ppriv);
mysja1000_set_bittiming(&m_ppriv);
mysja1000_open(&m_ppriv);    

这些函数,实现结构体的初始化以及相关寄存器的初始化操作。

调用 netdev_tx_t mysja1000_start_xmit(struct can_frame *cf,struct sja1000_priv *priv) 进行数据的发送。

调用 mysja1000_close()函数进行寄存器及中断的关闭操作

样例:

#include "mycan_drive.h"
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/ratelimit.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/serial_8250.h>
#include <linux/nmi.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/pm_runtime.h>

#include <asm/io.h>

#include <asm/serial.h>



static const struct can_bittiming_const mysja1000_bittiming_const = {
    .name = "",
    .tseg1_min = 1,
    .tseg1_max = 16,
    .tseg2_min = 1,
    .tseg2_max = 8,
    .sjw_max = 4,
    .brp_min = 1,
    .brp_max = 64,
    .brp_inc = 1,
};



struct sja1000_priv m_ppriv;


extern struct uart_8250_port canserial_ports[15];



static int can_update_spt(const struct can_bittiming_const *btc,
              int sampl_pt, int tseg, int *tseg1, int *tseg2)
{
    *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000;
    if (*tseg2 < btc->tseg2_min)
        *tseg2 = btc->tseg2_min;
    if (*tseg2 > btc->tseg2_max)
        *tseg2 = btc->tseg2_max;
    *tseg1 = tseg - *tseg2;
    if (*tseg1 > btc->tseg1_max) {
        *tseg1 = btc->tseg1_max;
        *tseg2 = tseg - *tseg1;
    }
    return 1000 * (tseg + 1 - *tseg2) / (tseg + 1);
}



static int can_calc_bittiming(struct can_priv *priv, struct can_bittiming *bt,
                  const struct can_bittiming_const *btc)
{

    printk("Enter can_calc_bittiming
");

    long best_error = 1000000000, error = 0;
    int best_tseg = 0, best_brp = 0, brp = 0;
    int tsegall, tseg = 0, tseg1 = 0, tseg2 = 0;
    int spt_error = 1000, spt = 0, sampl_pt;
    long rate;
    u64 v64;

    bt->bitrate = 1000000;
    /* Use CiA recommended sample points */
    if (bt->sample_point) {
        sampl_pt = bt->sample_point;
    } else {
        if (bt->bitrate > 800000)
            sampl_pt = 750;
        else if (bt->bitrate > 500000)
            sampl_pt = 800;
        else
            sampl_pt = 875;
    }

    /* tseg even = round down, odd = round up */
    for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
         tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) {
        tsegall = 1 + tseg / 2;
        /* Compute all possible tseg choices (tseg=tseg1+tseg2) */
        brp = priv->clock.freq / (tsegall * bt->bitrate) + tseg % 2;
        /* chose brp step which is possible in system */
        brp = (brp / btc->brp_inc) * btc->brp_inc;
        if ((brp < btc->brp_min) || (brp > btc->brp_max))
            continue;
        rate = priv->clock.freq / (brp * tsegall);
        error = bt->bitrate - rate;
        /* tseg brp biterror */
        if (error < 0)
            error = -error;
        if (error > best_error)
            continue;
        best_error = error;
        if (error == 0) {
            spt = can_update_spt(btc, sampl_pt, tseg / 2,
                         &tseg1, &tseg2);
            error = sampl_pt - spt;
            if (error < 0)
                error = -error;
            if (error > spt_error)
                continue;
            spt_error = error;
        }
        best_tseg = tseg / 2;
        best_brp = brp;
        if (error == 0)
            break;
    }

    // if (best_error) {
    //     /* Error in one-tenth of a percent */
    //     error = (best_error * 1000) / bt->bitrate;
    //     if (error > CAN_CALC_MAX_ERROR) {
    //         // netdev_err(dev,
    //         //        "bitrate error %ld.%ld%% too high
",
    //         //        error / 10, error % 10);
    //         return -EDOM;
    //     } else {
    //         // netdev_warn(dev, "bitrate error %ld.%ld%%
",
    //         //         error / 10, error % 10);
    //     }
    // }

    /* real sample point */
    bt->sample_point = can_update_spt(btc, sampl_pt, best_tseg,
                      &tseg1, &tseg2);

    v64 = (u64)best_brp * 1000000000UL;
    do_div(v64, priv->clock.freq);
    bt->tq = (u32)v64;
    bt->prop_seg = tseg1 / 2;
    bt->phase_seg1 = tseg1 - bt->prop_seg;
    bt->phase_seg2 = tseg2;

    /* check for sjw user settings */
    if (!bt->sjw || !btc->sjw_max)
        bt->sjw = 1;
    else {
        /* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */
        if (bt->sjw > btc->sjw_max)
            bt->sjw = btc->sjw_max;
        /* bt->sjw must not be higher than tseg2 */
        if (tseg2 < bt->sjw)
            bt->sjw = tseg2;
    }

    bt->brp = best_brp;
    /* real bit-rate */
    bt->bitrate = priv->clock.freq / (bt->brp * (tseg1 + tseg2 + 1));

   printk("can_calc_bittiming bitrate:%d
",bt->bitrate);

    return 0;
}




int mysja1000_set_bittiming(struct sja1000_priv *priv)
{
    

    printk("Enter mysja1000_set_bittiming
");
    can_calc_bittiming(&priv->can,&priv->can.bittiming,&mysja1000_bittiming_const);



    struct can_bittiming *bt = &priv->can.bittiming;
    u8 btr0, btr1;

    btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
    btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
        (((bt->phase_seg2 - 1) & 0x7) << 4);
    if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
        btr1 |= 0x80;

    // netdev_info(dev, "setting BTR0=0x%02x BTR1=0x%02x
", btr0, btr1);

    printk("mysja1000_set_bittiming BTR0=0x%02x BTR1=0x%02x
",btr0,btr1);
    priv->write_reg(priv, SJA1000_BTR0, btr0);
    priv->write_reg(priv, SJA1000_BTR1, btr1);

    return 0;
}



// 对发送接收计数失败的统计
int mysja1000_get_berr_counter(const struct sja1000_priv *priv,
                struct can_berr_counter *bec)
{
    
    bec->txerr = priv->read_reg(priv, SJA1000_TXERR);
    bec->rxerr = priv->read_reg(priv, SJA1000_RXERR);

    return 0;
}





/* Read SJA1000 register */
static u8 mytscan1_read(const struct sja1000_priv *priv, int reg)
{
    return inb((unsigned long)priv->reg_base + reg);
}

/* Write SJA1000 register */
static void mytscan1_write(const struct sja1000_priv *priv, int reg, u8 val)
{
    outb(val, (unsigned long)priv->reg_base + reg);
}

void myset_normal_mode(struct sja1000_priv *priv)
{

    printk("Enter myset_normal_mode
 ");
    unsigned char status = priv->read_reg(priv, SJA1000_MOD);
    u8 mod_reg_val = 0x00;
    int i;

    for (i = 0; i < 100; i++) {
        /* check reset bit */
        if ((status & MOD_RM) == 0) {
            priv->can.state = CAN_STATE_ERROR_ACTIVE;
            /* enable interrupts */
            if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
                priv->write_reg(priv, SJA1000_IER, IRQ_ALL);
            else
                priv->write_reg(priv, SJA1000_IER,
                        IRQ_ALL & ~IRQ_BEI);
            return;
        }

        /* set chip to normal mode */
        if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
            mod_reg_val |= MOD_LOM;
        if (priv->can.ctrlmode & CAN_CTRLMODE_PRESUME_ACK)
            mod_reg_val |= MOD_STM;
        priv->write_reg(priv, SJA1000_MOD, mod_reg_val);

        udelay(10);

        status = priv->read_reg(priv, SJA1000_MOD);
    }

}



void myset_reset_mode(struct sja1000_priv *priv)
{

    unsigned char status = priv->read_reg(priv, SJA1000_MOD);
    int i;

    /* disable interrupts */
    priv->write_reg(priv, SJA1000_IER, IRQ_OFF);

    for (i = 0; i < 100; i++) {
        /* check reset bit */
        if (status & MOD_RM) {
            priv->can.state = CAN_STATE_STOPPED;
            return;
        }

        /* reset chip */
        priv->write_reg(priv, SJA1000_MOD, MOD_RM);
        udelay(10);
        status = priv->read_reg(priv, SJA1000_MOD);
    }

}


// open can  



/******************************************************************************/

void mysja1000_write_cmdreg(struct sja1000_priv *priv, u8 val)
{
    unsigned long flags;

    /*
     * The command register needs some locking and time to settle
     * the write_reg() operation - especially on SMP systems.
     */
    spin_lock_irqsave(&priv->cmdreg_lock, flags);
    priv->write_reg(priv, SJA1000_CMR, val);
    priv->read_reg(priv, SJA1000_SR);
    spin_unlock_irqrestore(&priv->cmdreg_lock, flags);
}
/*
 * initialize SJA1000 chip:
 *   - reset chip
 *   - set output mode
 *   - set baudrate
 *   - enable interrupts
 *   - start operating mode
 */
void mychipset_init(struct sja1000_priv *priv )
{

    printk("Enter the mychipset_init
");

    /* set clock divider and output control register */
#if defined(CONFIG_LS1X_CAN0) || defined(CONFIG_LS1X_CAN1)
    mysja1000_write_cmdreg(priv, 0x80);
#else
    priv->write_reg(priv, SJA1000_CDR, priv->cdr | CDR_PELICAN);
#endif

    /* set acceptance filter (accept all) */
    priv->write_reg(priv, SJA1000_ACCC0, 0x00);
    priv->write_reg(priv, SJA1000_ACCC1, 0x00);
    priv->write_reg(priv, SJA1000_ACCC2, 0x00);
    priv->write_reg(priv, SJA1000_ACCC3, 0x00);

    priv->write_reg(priv, SJA1000_ACCM0, 0xFF);
    priv->write_reg(priv, SJA1000_ACCM1, 0xFF);
    priv->write_reg(priv, SJA1000_ACCM2, 0xFF);
    priv->write_reg(priv, SJA1000_ACCM3, 0xFF);
    priv->write_reg(priv, SJA1000_OCR, priv->ocr | OCR_MODE_NORMAL);
}







void mysja1000_start(struct sja1000_priv *priv)
{

    
    printk("Enter the mysja1000_start
");


    /* leave reset mode */
    if (priv->can.state != CAN_STATE_STOPPED)
        myset_reset_mode(priv);

    /* Initialize chip if uninitialized at this stage */
    if (!(priv->read_reg(priv, SJA1000_CDR) & CDR_PELICAN))
        mychipset_init(priv);

    /* Clear error counters and error code capture */
    priv->write_reg(priv, SJA1000_TXERR, 0x0);
    priv->write_reg(priv, SJA1000_RXERR, 0x0);
    priv->read_reg(priv, SJA1000_ECC);

    /* leave reset mode */
    myset_normal_mode(priv);
}


int mysja1000_set_mode(struct sja1000_priv *priv, enum can_mode mode)
{
    switch (mode) {
    case CAN_MODE_START:
        mysja1000_start(priv);
        // if (netif_queue_stopped(dev))
        //     netif_wake_queue(dev);
        break;

    default:
        return -EOPNOTSUPP;
    }

    return 0;
}



static u8 sp_read_reg8(const struct sja1000_priv *priv, int reg)
{
    return ioread8(priv->reg_base + reg);
}

static void sp_write_reg8(const struct sja1000_priv *priv, int reg, u8 val)
{
    iowrite8(val, priv->reg_base + reg);
}








int calc_com_addr(int can_id)
{
     int device_addr, sub_addr;
     device_addr = can_id & 0x0F;
     sub_addr = (can_id & 0x70)>>4;

     return sub_addr+(device_addr-1)*5;
}











void mysja1000_rx(struct sja1000_priv *priv)
{

    // struct net_device_stats *stats = &dev->stats;
    // struct can_frame can_buff;
    char length_can;
    // struct can_frame *cf = &can_buff;
    struct sk_buff skb;
    uint8_t fi;
    uint8_t dreg;
    canid_t id;
    int i;
    unsigned ch;
    printk("Enter the mysja1000_rx
");

/*******************************/
    // 进行数据的获取
    /* create zero'ed CAN frame buffer */
    // skb = alloc_can_skb(dev, &cf);

    // if (skb == NULL)
    //     return;

    fi = priv->read_reg(priv, SJA1000_FI);

    if (fi & SJA1000_FI_FF) {
        printk("mysja1000_rx expand frame
");
        /* extended frame format (EFF) */
        dreg = SJA1000_EFF_BUF;
        id = (priv->read_reg(priv, SJA1000_ID1) << 21)
            | (priv->read_reg(priv, SJA1000_ID2) << 13)
            | (priv->read_reg(priv, SJA1000_ID3) << 5)
            | (priv->read_reg(priv, SJA1000_ID4) >> 3);
        id |= CAN_EFF_FLAG;
    } else {
        /* standard frame format (SFF) */
        dreg = SJA1000_SFF_BUF;
        id = (priv->read_reg(priv, SJA1000_ID1) << 3)
            | (priv->read_reg(priv, SJA1000_ID2) >> 5);
    }

    // cf->can_dlc = get_can_dlc(fi & 0x0F);


    length_can = get_can_dlc(fi & 0x0F);


    int curr_index =  calc_com_addr(id);

    printk("receive from can's com addr:%d
",curr_index);
    struct uart_port *curr_port = &canserial_ports[curr_index].port;

    if (fi & SJA1000_FI_RTR) {
        id |= CAN_RTR_FLAG;
    } else {
        for (i = 0; i < length_can; i++)
        {

            ch = priv->read_reg(priv, dreg++);

            uart_insert_char(curr_port, 0, 0x02, ch, 0);

            curr_port->icount.tx++;
            // cf->data[i] = priv->read_reg(priv, dreg++);
            // printk("rx data %x
",data[i]);
        }

    }

    spin_unlock(&curr_port->lock);
    tty_flip_buffer_push(&curr_port->state->port);
    spin_lock(&curr_port->lock);


    /* release receive buffer */
    mysja1000_write_cmdreg(priv, CMD_RRB);



}




irqreturn_t mysja1000_interrupt(int irq, void *dev_id)
{



     struct sja1000_priv *priv = (struct sja1000_priv*)dev_id;

     printk("mysja1000_interrupt
");



    // struct net_device_stats *stats = &dev->stats;

    uint8_t isrc, status;
    int n = 0;

    if (priv->pre_irq)
        priv->pre_irq(priv);

    /* Shared interrupts and IRQ off? */
    if (priv->read_reg(priv, SJA1000_IER) == IRQ_OFF)
        goto out;

    while ((isrc = priv->read_reg(priv, SJA1000_IR)) &&
           (n < SJA1000_MAX_IRQ)) {

        status = priv->read_reg(priv, SJA1000_SR);
        /* check for absent controller due to hw unplug */
        // if (status == 0xFF && sja1000_is_absent(priv))
        //     goto out;

        // if (isrc & IRQ_WUI)
        //     netdev_warn(dev, "wakeup interrupt
");

        if (isrc & IRQ_TI) {
            /* transmission buffer released */
            if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT &&
                !(status & SR_TCS)) {
                // stats->tx_errors++;
                // can_free_echo_skb(dev, 0);
            } else {
                /* transmission complete */
                // stats->tx_bytes +=
                    priv->read_reg(priv, SJA1000_FI) & 0xf;
                // stats->tx_packets++;
            }
            // can_led_event(dev, CAN_LED_EVENT_TX);
        }
        if (isrc & IRQ_RI) {
            /* receive interrupt */
            while (status & SR_RBS) {
                mysja1000_rx(priv);
                status = priv->read_reg(priv, SJA1000_SR);
                /* check for absent controller */
                // if (status == 0xFF && sja1000_is_absent(priv))
                //     goto out;
            }
        }
        if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) {
            /* error interrupt */
            // if (sja1000_err(dev, isrc, status))
            //     break;
        }
        n++;
    }


out:
    if (priv->post_irq)
        priv->post_irq(priv);

    // if (n >= SJA1000_MAX_IRQ)
    //     netdev_dbg(dev, "%d messages handled in ISR", n);

    return (n) ? IRQ_HANDLED : IRQ_NONE;
}



EXPORT_SYMBOL_GPL(mysja1000_interrupt);


int mysja1000_open(struct sja1000_priv *priv)
{


    printk("enter mysja1000_open
");

    myset_reset_mode(priv);



/***********************open_candev()**********************/
    // err = open_candev(dev); 等效
    if (!priv->can.bittiming.bitrate) {
        printk("bit-timing not yet defined
");
        return -EINVAL;
    }

    printk("priv->can.bittiming.bitrate is %d
" ,priv->can.bittiming.bitrate);

    if ((priv->can.ctrlmode & CAN_CTRLMODE_FD) &&
        (!priv->can.data_bittiming.bitrate ||
         (priv->can.data_bittiming.bitrate < priv->can.bittiming.bitrate))) {
            // netdev_err(dev, "incorrect/missing data bit-timing
");
        printk("incorrect/missing data bit-timing
");
        return -EINVAL;
    }


    // mysja1000_set_mode(priv, CAN_MODE_START);
    
/*********************************************/


// 请求中断   待解决
// 第一个参数irq:申请的硬件中断号;

// 第二个参数handler:是一个函数指针,向系统登记的中断处理函数,是一个回调函数,当中断发生时,系统调用这个函数,传入的参数包括中断设备 id,寄存器值。

// 第三个参数flags:指定了快速中断或中断共享等中断处理属性。

// 第四个参数devices:指定设备驱动程序的名称。

// 第五个参数dev_id:传入中断处理程序的参数,可以为NULL,在注册共享中断时,此参数不能为NULL,作为共享中断时的中断区别参数。
    int err = request_irq(14, mysja1000_interrupt, priv->irq_flags,
                  "canserial8250", (void *)priv);
   
    if(!err)
    {
        printk("request_irq failed
");
    }

    mysja1000_start(priv);

}






int mysja1000_close(struct sja1000_priv *priv)
{


    printk("mysja1000_close
");
    myset_reset_mode(priv);

    if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER))
        free_irq(14, (void *)priv);

    // close_candev(dev);


    return 0;
}





/*
 * transmit a CAN message
 * message layout in the sk_buff should be like this:
 * xx xx xx xx     ff     ll   00 11 22 33 44 55 66 77
 * [  can-id ] [flags] [len] [can data (up to 8 bytes]
 */
// netdev_tx_t mysja1000_start_xmit(struct sk_buff *skb,
                        // struct sja1000_priv *priv)

netdev_tx_t mysja1000_start_xmit(struct can_frame *cf,
                        struct sja1000_priv *priv)
{

 //  对sk_buff 结构体定义其id、can_dlc、data

    // struct can_frame *cf = (struct can_frame *)skb->data;

    uint8_t fi;
    uint8_t dlc;
    canid_t id;
    uint8_t dreg;
    u8 cmd_reg_val = 0x00;
    int i;
    printk("Enter the mysja1000_start_xmit
");
    printk("the start send data:%s
",cf->data);


    // if (can_dropped_invalid_skb(dev, skb))
    //     return NETDEV_TX_OK;

    // netif_stop_queue(dev);

    fi = dlc = cf->can_dlc;
    id = cf->can_id;

    if (id & CAN_RTR_FLAG)
        fi |= SJA1000_FI_RTR;

// CAN_EFF_FLAG 扩展帧  用扩展帧对数据进行发送
    if (id & CAN_EFF_FLAG) {
        printk("Enter mysja1000_start_xmit expand frame 
");
        fi |= SJA1000_FI_FF;
        dreg = SJA1000_EFF_BUF;
        priv->write_reg(priv, SJA1000_FI, fi);
        priv->write_reg(priv, SJA1000_ID1, (id & 0x1fe00000) >> 21);
        priv->write_reg(priv, SJA1000_ID2, (id & 0x001fe000) >> 13);
        priv->write_reg(priv, SJA1000_ID3, (id & 0x00001fe0) >> 5);
        priv->write_reg(priv, SJA1000_ID4, (id & 0x0000001f) << 3);
    } else {
        dreg = SJA1000_SFF_BUF;
        priv->write_reg(priv, SJA1000_FI, fi);
        priv->write_reg(priv, SJA1000_ID1, (id & 0x000007f8) >> 3);
        priv->write_reg(priv, SJA1000_ID2, (id & 0x00000007) << 5);
    }

    for (i = 0; i < dlc; i++)
    { 
        printk("send data:%d
", cf->data[i]);
        priv->write_reg(priv, dreg++, cf->data[i]);
    }

    // can_put_echo_skb(skb, dev, 0);

    if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
        cmd_reg_val |= CMD_AT;

    if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
        cmd_reg_val |= CMD_SRR;
    else
        cmd_reg_val |= CMD_TR;

    mysja1000_write_cmdreg(priv, cmd_reg_val);

    return NETDEV_TX_OK;
}


int sja1000_is_absent(struct sja1000_priv *priv)
{    
    printk("sja1000_is_absent
");
    return (priv->read_reg(priv, SJA1000_MOD) == 0xFF);
}


int myregister_sja1000dev(struct sja1000_priv *priv)
{
    int ret;

    printk("Enter myregister_sja1000dev
");
    // if (!mysja1000_probe_chip(priv))
    //     return -ENODEV;

    if (priv->reg_base && sja1000_is_absent(priv)) {
        printk("probing failed
");
        return 0;
    }


    // dev->flags |= IFF_ECHO;     we support local echo 
    // dev->netdev_ops = &sja1000_netdev_ops;

    myset_reset_mode(priv);
    mychipset_init(priv);

    // ret =  register_candev(dev);

    // if (!ret)
        // devm_can_led_init(dev);

    // return ret;
}



// probe 这一块没什么问题
int mytscan1_probe(struct sja1000_priv *priv)
{

    printk("Enter mytscan1_probe
");
    priv->irq_flags = 0;
    // dev->irq = 14;
    priv->reg_base = (void __iomem *)0xbfe50000;
    priv->can.clock.freq = 41406250;
    priv->ocr = 88;
    priv->cdr = 64;

    priv->read_reg = sp_read_reg8;
    priv->write_reg = sp_write_reg8;



//alloc_sja1000dev
    priv->dev = NULL;
    priv->can.bittiming_const = &mysja1000_bittiming_const;
    priv->can.do_set_bittiming = mysja1000_set_bittiming;
    priv->can.do_set_mode = mysja1000_set_mode;
    priv->can.do_get_berr_counter = mysja1000_get_berr_counter;
    priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
                       CAN_CTRLMODE_LISTENONLY |
                       CAN_CTRLMODE_3_SAMPLES |
                       CAN_CTRLMODE_ONE_SHOT |
                       CAN_CTRLMODE_BERR_REPORTING |
                       CAN_CTRLMODE_PRESUME_ACK;



   myregister_sja1000dev(priv);

    return 0;
}

    

原文地址:https://www.cnblogs.com/hzijone/p/5242455.html