之前開發專案的時候,碰到一個IR remote control到底該用polling還是interrupt的方式處理,polling缺點像是固定要消耗時間去檢查GPIO是不是有收到資料,如果時間間隔不恰當,可能就漏掉重要的資料了.用interrupt的話,就有點浪費珍貴的interrupt資源.不過後來實作方還是選了後者來實作.現在有一點時間,回頭去看看當時負責這塊的人寫的東西.下面的圖是NEC IR的編碼方式.

 

ir rec

A logical "1" takes 2.25ms to transmit, while a logical "0" is only half of that, being 1.125ms.

The picture above shows a typical pulse train of the NEC protocol. With this protocol the LSB is transmitted first. In this case Address $59 and Command $16 is transmitted. A message is started by a 9ms AGC burst, which was used to set the gain of the earlier IR receivers. This AGC burst is then followed by a 4.5ms space, which is then followed by the Address and Command. Address and Command are transmitted twice. The second time all bits are inverted and can be used for verification of the received message. The total transmission time is constant because every bit is repeated with its inverted length. If you're not interested in this reliability you can ignore the inverted values, or you can expand the Address and Command to 16 bits each!

A command is transmitted only once, even when the key on the remote control remains pressed. Every 110ms a repeat code is transmitted for as long as the key remains down. This repeat code is simply a 9ms AGC pulse followed by a 2.25ms space and a 560µs burst.

NEC Protocol

misc driver實作:

static atomic_t reference_count = ATOMIC_INIT(0);
#define CIR_GPIO                GPIO(0)
#define CIR_MINOR                255
#define CIR_NAME                "cir"
#define CIR_DATA_BITS            32    
#define CIR_BUFFER_SIZE            16

#define IR_STATE_0                0  // wait first falling edge.
#define IR_STATE_1                1  // first falling edge received. 
#define IR_STATE_2                2  // pre-pulse detected, measure gap time.
#define IR_STATE_3                3  // normal message received. 

struct cir_device_data {
    struct timeval lasttv;
    
    unsigned char head;
    unsigned char tail;
    
    unsigned long complete_bits;
    unsigned long buff[CIR_BUFFER_SIZE];
    
    int ir_state;
    int ir_count;
    
    wait_queue_head_t    waitq;
    struct mutex mutex;
    
};
static struct file_operations cir_fops = {
    .owner        = THIS_MODULE,
    .open        = cir_open,
    .release    = cir_release,
    .read        = cir_read,
    .poll        = cir_poll,
};

static struct miscdevice cir_misc = {
    .fops   = &cir_fops,
    .minor  = CIR_MINOR,
    .name   = CIR_NAME,
};

static int __init cir_init(void)
{
    return misc_register(&cir_misc);    
}

static void __exit cir_exit(void)
{
    misc_deregister(&cir_misc);
}

cir_open初始化module,最重要的是註冊interrupt handler,

依據不同的狀態,決定interrupt是 Rising edge or Falling edge

static irqreturn_t cir_irq_handler (int irq, void *dev_id, struct pt_regs *regs)
{
    struct timeval tv;
    long deltv;
    int data;
    struct cir_device_data *dev;
    
    dev = (struct cir_device_data *)dev_id;
    
    /* get current time */
    do_gettimeofday(&tv);
    
    if(dev->ir_state == IR_STATE_0) {
        dev->ir_state = IR_STATE_1;
        /* Initializate ir variable */
        dev->ir_count = 0; 
        dev->complete_bits = 0;
        
        /* Configure to generate an interrupt on rising edge
         * Wait 9ms pre-pulse.   
         */
        set_irq_type(gpio_to_irq(CIR_GPIO), IRQT_RISING);
    } else {
        deltv = tv.tv_sec - dev->lasttv.tv_sec;
        
        /* calc time since last interrupt in microseconds */
        data = (int) (deltv*1000000 + tv.tv_usec - dev->lasttv.tv_usec);
        decode_process(dev, data);
    }
    
    /* restore time */
    dev->lasttv = tv;
    
    return IRQ_HANDLED;
}

static int cir_open(struct inode *inode, struct file *file)
{
    struct cir_device_data *cir_dev;
    int retval = 0;
    
    if (file->f_mode == FMODE_WRITE) {
        printk (KERN_ERR "TX Not supported\n");
        return -EACCES;
    }
    
    /* current device driver counter*/
    if (atomic_inc_return(&reference_count) > 1) {
        atomic_dec(&reference_count);
        return -EACCES;
    }
    
    /* allocate memory for subchannel data */
    cir_dev = kzalloc(sizeof (struct cir_device_data), GFP_KERNEL);
    if (cir_dev == NULL) {
        printk(KERN_ERR "%s: couldn't allocate device data\n",
               __FUNCTION__);
        return -ENOMEM;
    }
    
    /* Initialization device */
    cir_dev->ir_state = IR_STATE_0;
    mutex_init(&cir_dev->mutex);
    init_waitqueue_head(&cir_dev->waitq);
    
    gpio_direction_input(CIR_GPIO);
    
    /* Configure to generate an interrupt on falling edge only */
    set_irq_type(gpio_to_irq(CIR_GPIO), IRQT_FALLING);
    
    retval = request_irq(IRQ_DM3XX_GPIO0, cir_irq_handler,
                           IRQF_DISABLED, "Consumer IR", (void *)cir_dev);
    if (retval < 0) {
        kfree(cir_dev);
        atomic_dec(&reference_count);
        printk(KERN_ERR "CIR request irq failed\n");
        return -EFAULT;
    }
    
    file->private_data = cir_dev;
        
    return 0;
}

 

cir_poll提供上層Ap可以用select的方式確認有無資料.

static unsigned int cir_poll(struct file *file, poll_table *wait)
{
    struct cir_device_data *cir_dev = file->private_data;

    poll_wait(file, &cir_dev->waitq, wait);

    if (cir_dev->head != cir_dev->tail) {
        return POLLIN | POLLRDNORM;
    }
    
    return 0;
}

最後是最主要parsing的部份

static int check_bit(int interval)
{
    int bit = 0;

    /* if interval is 1.12ms(+-20%) then bit 0 */
    if(interval > 896 && interval < 1344) 
        bit = 0;
    /* if interval is 2.25ms(+-20%) then bit 1 */
    else if(interval > 1800 && interval < 2700)
        bit = 1;
    else
    /* invalid data bit */    
        bit = -1;  
    
    return bit;
}

static void decode_process(struct cir_device_data *dev, int interval)
{
    int bitvalue = 0;
    
    switch(dev->ir_state){
        case IR_STATE_1:
            /* Configure to generate an interrupt on falling edge
              * decision 9ms pre-pulse, within a +-20% range of the nominal value
              * 9000 - (9000*0.2) = 7200, 9000+(9000*0.2) = 9900
              */
            set_irq_type(gpio_to_irq(CIR_GPIO), IRQT_FALLING);
            if(interval > 7200 && interval < 10800) 
                dev->ir_state = IR_STATE_2; // pre-pulse detected.
            else 
                dev->ir_state = IR_STATE_0; // exit with error, return state_0
            break;
        case IR_STATE_2:
           /* decision long interval(4.5ms) or short interval (2.25ms)
             * long interval is normal message, short interval is repetition code.
             * 4500 - (4500*0.2) = 3600, 4500 + (4500*0.2) = 5400
             * 2250 - (2250*0.2) = 1800, 2250 + (2250*0.2) = 2700
             */
            if(interval > 3600 && interval < 5400) {  
                dev->ir_state = IR_STATE_3; 
            } else if(interval > 1800 && interval < 2700) {
                /* driver not supported repetition. 
                   * Return state_0
                   */
                dev->ir_state = IR_STATE_0; //repeat state.
            } else {
                dev->ir_state = IR_STATE_0;     // exit with error, return state_0
                printk(KERN_DEBUG "CIR:exit with error!\n");
            }
            break;
        case IR_STATE_3:
           /* 32 bits entire message receiving
             */
             bitvalue = check_bit(interval);
             if(bitvalue < 0) {
                 /* invalid data bit */ 
                 dev->ir_state = IR_STATE_0;
             } else {
                 dev->complete_bits <<= 1;
                 dev->complete_bits |= bitvalue;
                 dev->ir_state = IR_STATE_3;
                 dev->ir_count++;
                 
                 if(dev->ir_count >= CIR_DATA_BITS) {
                     /* receiving complete */ 
                     dev->ir_state = IR_STATE_0;
                     /* store to buffer */
                     dev->buff[dev->head] = dev->complete_bits;
                    dev->head = (dev->head + 1) % CIR_BUFFER_SIZE;
                     wake_up_interruptible(&dev->waitq);
                 } 
             }
            break;
        default:
            break;
    }
}

還有cir_read提供讀取command的內容

static ssize_t cir_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
    struct cir_device_data *cir_dev = file->private_data;
    int retval = 0;
    
    if (cir_dev->head == cir_dev->tail && (file->f_flags & O_NONBLOCK))
        return -EAGAIN;
    
    retval = wait_event_interruptible(cir_dev->waitq, cir_dev->head != cir_dev->tail);
    if (retval)
        return retval;
    
    retval = mutex_lock_interruptible(&cir_dev->mutex);
    if (retval)
        return retval;

    while (cir_dev->head != cir_dev->tail && retval + sizeof(unsigned long) <= count) {
        if (copy_to_user(buffer + retval, &cir_dev->buff[cir_dev->tail], sizeof(unsigned long))) {
            retval = -EFAULT;
            goto out;
        }
        cir_dev->tail = (cir_dev->tail + 1) % CIR_BUFFER_SIZE;
        retval += sizeof(unsigned long);
    }

 out:
    mutex_unlock(&cir_dev->mutex);

    return retval;
}
arrow
arrow
    全站熱搜

    Person 發表在 痞客邦 留言(1) 人氣()