曲径通幽论坛

标题: 输入输出复用实例 [打印本页]

作者: beyes    时间: 2009-11-9 20:02
标题: 输入输出复用实例
使用打印机端口来测试 poll()函数处理输入输出的复用.
测试用的应用程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/poll.h>

int main()
{
    int    dev;
    struct pollfd    events [1];
    int    retval;
    char    buff [128];
    int    readcnt;
    int    loop;
    int    flashing;

    printf ("poll Program Start\n");
    dev = open ("/dev/polldev", O_RDWR);    /*可阻塞打开设备*/

    if (dev < 0) {
        printf ("[/dev/polldev] Open fail\n");
        exit (-1);
    }

    flashing = 0;
    printf ("wait poll \n");
    buff [0] = 0xff;
    write (dev, buff, 1);

    while (1) {
        memset (events, 0, sizeof (events));
        events[0].fd = dev;
        events[0].events = POLLIN;    /*对有数据读入感兴趣*/
       
        retval = poll ((struct pollfd *) &events, 1, 1000);
       
        if (retval < 0) {        /*出错*/
            perror ("poll error: ");
            exit (EXIT_FAILURE);
        }

        if (retval == 0) {        /*1s 内 poll 不到数据*/
            flashing = !flashing;
            if (flashing)
                buff[0] = 0x00;
            else   
                buff[0] = 0xff;
            write (dev, buff, 1);
            continue;
        }

        if (events[0].revents & POLLERR) {
            printf ("Device Error\n");
            exit (EXIT_FAILURE);
        }

        if (events[0].revents & POLLIN) {    /*有数据可读*/
            readcnt = read (dev, buff, sizeof (buff));
            printf ("READ DATA COUNT [%d]\n", readcnt);

            for (loop = 0; loop < readcnt; loop++) {
                printf ("READ DATA [%02X]\n", buff [loop]);
            }
        }
    }

        buff [0] = 0x00;
        write (dev, buff, 1);

        close (dev);
       
        return (0);
}
说明:
(1)  先用 write() 开启 LED , 然后进程再使用 poll() 进入睡眠状态,直到有输入数据.
(2)  没有发生数据的输入,且超时 1s ,进程就会被唤醒,从 poll() 函数种脱出, 接着控制 LED 开关. 如果一直都没数据,那么 LED 就会以 1s 为周期闪烁.
(3)  当接触第 10 和 第 25 两根引脚时,发生中断, 把打印机端口的状态写入环形队列中,然后唤醒进程.
(4)  输出读到的数据,然后继续循环.

驱动程序代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>

#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/interrupt.h>
#include <linux/wait.h>

#include <linux/time.h>
#include <linux/timer.h>

#include <linux/poll.h>

#define    POLL_DEV_NAME        "polldev"
#define POLL_DEV_MAJOR        240
#define POLL_WRITE_ADDR        0x378
#define POLL_READ_ADDR        0X379
#define POLL_CTRL_ADDR        0x37A

#define    POLL_IRQ        3
#define    POLL_IRQ_ENABLE_MASK    0x10

#define    POLL_BUFF_MAX        64

DECLARE_WAIT_QUEUE_HEAD(WaitQueue_Read);

#define MAX_QUEUE_CNT        128
static unsigned char ReadQ [MAX_QUEUE_CNT];
static unsigned long ReadQCount = 0;
static unsigned long ReadQHead = 0;
static unsigned long ReadQTail = 0;

irqreturn_t poll_interrupt (int irq, void *dev_id)
{
    unsigned long flags;

    local_save_flags(flags);
    local_irq_disable();

    if (ReadQCount < MAX_QUEUE_CNT) {
        ReadQ [ReadQHead] = (unsigned long) inb (POLL_READ_ADDR);    /*读数据并保存*/
        ReadQHead = (ReadQHead + 1) % MAX_QUEUE_CNT;           
        ReadQCount++;
    }
    local_irq_restore(flags);

    wake_up_interruptible(&WaitQueue_Read);

    return (IRQ_HANDLED);
}

int poll_open (struct inode *inode, struct file *filp)
{
    if (!request_irq (POLL_IRQ, poll_interrupt, IRQF_DISABLED, POLL_DEV_NAME, NULL));
        outb (POLL_IRQ_ENABLE_MASK, POLL_CTRL_ADDR);

    return (0);
}

ssize_t poll_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
    unsigned long flags;
    int    realmax;
    int    loop;
    int    retstate;

    if ((!ReadQCount) && (filp->f_flags & O_NONBLOCK))    /*无可读数据且应用程序不阻塞调用*/
        return (-EAGAIN);

    retstate = wait_event_interruptible(WaitQueue_Read, ReadQCount);    /*等待,直到 ReadQCount > 0*/
    if (retstate) return (retstate);    /*被信号打断返回-ERESTARTSYS,条件满足时返回0*/

    local_save_flags(flags);
    local_irq_disable();            /*处理可以被中断改变的全局变量时要先禁止中断*/

    realmax = 0;
    if (ReadQCount > 0) {
        if (ReadQCount <= count)
            realmax = ReadQCount;
        else
            realmax = count;    /*传递所有可读数据,应用程序read要求读取的数据可能会比实际可读的还要多*/

        for (loop = 0; loop < realmax; loop++) {
            put_user (ReadQ[ReadQTail], (char *) &buf[loop]);
            ReadQTail = (ReadQTail + 1) % MAX_QUEUE_CNT;
            ReadQCount--;
        }
    }
    local_irq_restore(flags);

    return (realmax);
}

ssize_t poll_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
    unsigned char status;
    int    loop;

    for (loop = 0; loop < count; loop++) {
        get_user (status, (char *)buf);   
        outb (status, POLL_WRITE_ADDR);
    }

    return (count);
}

unsigned int poll_poll (struct file *filp, poll_table *wait)
{
    unsigned int mask = 0;
   
    poll_wait (filp, &WaitQueue_Read, wait);

    if (ReadQCount > 0)
        mask |= (POLLIN | POLLRDNORM);

    return mask;
}

int poll_release (struct inode *inode, struct file *filp)
{
    outb (0x00, POLL_CTRL_ADDR);
    free_irq (POLL_IRQ, NULL);
    return (0);
}

struct file_operations poll_fops = {
    .owner        = THIS_MODULE,
    .read        = poll_read,
    .write        = poll_write,
    .poll        = poll_poll,
    .open        = poll_open,
    .release    = poll_release,
};

int poll_init (void)
{
    int result;

    result = register_chrdev (POLL_DEV_MAJOR, POLL_DEV_NAME, &poll_fops);
    if (result < 0)
        return (result);

    return (0);
}

void poll_exit (void)
{
    unregister_chrdev (POLL_DEV_MAJOR, POLL_DEV_NAME);
}

module_init(poll_init);
module_exit(poll_exit);

MODULE_LICENSE ("Dual BSD/GPL");
运行与输出:
root@beyes-groad:/home/beyes/Drivers/MyDrivers/poll_dev# ./poll_app.exe
poll Program Start
wait poll
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [3F]
READ DATA COUNT [1]
READ DATA [7F]
从输出结果可以看出,因为在读取 0x379 端口的时候,其 bit.7 位(11引脚)必为忙状态, 11 引脚上为 5V 电平,但经过一个非门,所以读到必为 0 .

关于输入输出复用的相关内容:
http://www.groad.net/bbs/read.php?tid-1351.html
关于 poll_wait() 函数的相关介绍:
http://www.groad.net/bbs/read.php?tid-1352.html




欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2