2012年9月2日 星期日

如何撰寫在Linux Kernel 2.6.x的GPIO驅動程式


前言
GPIO是目前SoC常常會使用到的一個元件,以往的GPIO使用上並不普遍,所以在Linux Kernel並未加以統一寫法,因此以往的寫法則各家自行撰寫,比較多的是架構在misc的驅動之下,但今日新的Linux Kernel則有給予新的統一做法,它讓kernel中有要用GPIO的其它驅動程式可以透過其定義的library API來使用,而GPIO本身則實現其實體層的控制就好,如此一來其它使用GPIO的驅動則在porting更容易了。

什麼是GPIO
對於GPIO我們要先有一些了解,GPIO – Generic Program Input Output,中文說法是可程式輸出輸入的控制訊號,意思是該點可以設定為input或者output訊號,而其輸出的訊號則是單純的數位訊號,不是high就是low,而輸入訊號的偵測也是一樣,而其電位則是TTL訊號,而這樣的東西,通常可以例如模擬I2C HOST,或者拿來讀取RTC的介面,又或接一個LED當做某些事件的指示使用。

Kernel中的GPIO Library
還没有寫驅動程式之前,我們先來了解這個library,它是最近的Linux kernel版本所定義的,是給其它需要用到GPIO的驅動程式所使用,意思就是在Kernel內部定義了義標準的GPIO Library API,如此一來對需要使用到GPIO的驅動程式便可以不用理會其底層的GPIO,也不會因平台不同而各家自行所定義的GPIO API而造成上層的驅動程式而跟著修改,我們可以用以下的軟體加構圖來了解其中的做法:












目前定義較常用的Kernel API有如下:
  • int gpio_direction_output(unsigned gpio, int value);
這是設定該GPIO為output,第一個參數為GPIO number,第二個參數則是設定其為輸出時的初始值。
  • int gpio_set_value(unsigned gpio, int value);
這是針對己經設為output的GPIO設定其值為high或者為low。
  • int gpio_direction_input(unsigned gpio);
這是設定該GPIO為input。
  • int gpio_get_value(unsigned gpio);
這是針對己設為input的GPIO讀回其現在的輸入值為何。
  • int gpio_request_array(struct gpio *array, size_t num);
這是若要使用任何GPIO時,必須先呼叫該API,這個動作有點類似open的動作,以免其它人會佔用到相同的GPIO pin,這個API可以讓你要求不連續任意個存在的GPIO,之後你就可以使用以上的所有API。
struct gpio {
        unsigned        gpio;
        unsigned long   flags;
        const char      *label;
};
flags可以設定的值如下:
#define GPIOF_DIR_OUT   (0 << 0)
#define GPIOF_DIR_IN    (1 << 0)

#define GPIOF_INIT_LOW  (0 << 1)
#define GPIOF_INIT_HIGH (1 << 1)

#define GPIOF_IN             (GPIOF_DIR_IN)
#define GPIOF_OUT_INIT_LOW (GPIOF_DIR_OUT | GPIOF_INIT_LOW)
#define GPIOF_OUT_INIT_HIGH (GPIOF_DIR_OUT | GPIOF_INIT_HIGH)
  • int gpio_free_array(struct gpio *array, size_t num);
上一個若類似open的動作,這一個API就是close的動作。
以上這些kernel API都是給kernel內部其它driver使用的,使用它之前必需確定GPIO驅動已經被載入,否則將會無法使用,所以這時驅動載入的順序是非常重要的。另外由於有這一層的介面,使得其它會使用到GPIO的驅動程式,會統一的介面,不會因為底層的GPIO控制驅動程式不同因而有不同的介面,而產生需要修改程式,這對軟體的維護會是一大負擔。

實體層的GPIO chip controller的驅動程式
這是一個底層的驅動程式,直接控制GPIO的晶片,並接受上層的管理層的指令動作,並以此提供其它需要使用GPIO的驅動程式,可以有統一的介面。該驅動程式會用到一個資料結構,它是宣告在include/asm-generic/gpio.h,並且在kernel中必需將CONFIG_GENERIC_GPIO啓動,其宣告如下:
/**
 * struct gpio_chip - abstract a GPIO controller
 * @label: for diagnostics
 * @dev: optional device providing the GPIOs
 * @owner: helps prevent removal of modules exporting active GPIOs
 * @request: optional hook for chip-specific activation, such as
 *      enabling module power and clock; may sleep
 * @free: optional hook for chip-specific deactivation, such as
 *      disabling module power and clock; may sleep
 * @direction_input: configures signal "offset" as input, or returns error
 * @get: returns value for signal "offset"; for output signals this
 *      returns either the value actually sensed, or zero
 * @direction_output: configures signal "offset" as output, or returns error
 * @set: assigns output value for signal "offset"
 * @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
 *      implementation may not sleep
 * @dbg_show: optional routine to show contents in debugfs; default code
 *      will be used when this is omitted, but custom code can show extra
 *      state (such as pullup/pulldown configuration).
 * @base: identifies the first GPIO number handled by this chip; or, if
 *      negative during registration, requests dynamic ID allocation.
 * @ngpio: the number of GPIOs handled by this controller; the last GPIO
*      handled is (base + ngpio - 1).
 * @can_sleep: flag must be set iff get()/set() methods sleep, as they
 *      must while accessing GPIO expander chips over I2C or SPI
 * @names: if set, must be an array of strings to use as alternative
 *      names for the GPIOs in this chip. Any entry in the array
 *      may be NULL if there is no alias for the GPIO, however the
 *      array must be @ngpio entries long.  A name can include a single printk
 *      format specifier for an unsigned int.  It is substituted by the actual
 *      number of the gpio.
 *
 * A gpio_chip can help platforms abstract various sources of GPIOs so
 * they can all be accessed through a common programing interface.
 * Example sources would be SOC controllers, FPGAs, multifunction
 * chips, dedicated GPIO expanders, and so on.
 *
 * Each chip controls a number of signals, identified in method calls
 * by "offset" values in the range 0..(@ngpio - 1).  When those signals
 * are referenced through calls like gpio_get_value(gpio), the offset
 * is calculated by subtracting @base from the gpio number.
 */
struct gpio_chip {
        const char              *label;
        struct device           *dev;
        struct module           *owner;

        int                     (*request)(struct gpio_chip *chip,
                                                unsigned offset);
        void                    (*free)(struct gpio_chip *chip,
                                                unsigned offset);

        int                     (*direction_input)(struct gpio_chip *chip,
                                                unsigned offset);
        int                     (*get)(struct gpio_chip *chip,
                                                unsigned offset);
        int                     (*direction_output)(struct gpio_chip *chip,
                                                unsigned offset, int value);
        int                     (*set_debounce)(struct gpio_chip *chip,
                                                unsigned offset, unsigned debounce);

        void                    (*set)(struct gpio_chip *chip,
                                                unsigned offset, int value);
        int                     (*to_irq)(struct gpio_chip *chip,
                                                unsigned offset);

        void                    (*dbg_show)(struct seq_file *s,
                                                struct gpio_chip *chip);
        int                     base;
        u16                     ngpio;
        const char              *const *names;
        unsigned                can_sleep:1;
        unsigned                exported:1;

#if defined(CONFIG_OF_GPIO)
        /*
         * If CONFIG_OF is enabled, then all GPIO controllers described in the
         * device tree automatically may have an OF translation
         */
        struct device_node *of_node;
        int of_gpio_n_cells;
        int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
                        const void *gpio_spec, u32 *flags);
#endif
};
另外向上註冊及移除的API如下:
/* add/remove chips */
extern int gpiochip_add(struct gpio_chip *chip);
extern int __must_check gpiochip_remove(struct gpio_chip *chip);
extern struct gpio_chip *gpiochip_find(void *data, int (*match)(struct gpio_chip *chip, void *data));

程式範例
GPIO控制晶片驅動程式:
struct myart_gpio_module {
struct gpio_chip gpio;
……………………………….
};

int myart_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct myart_gpio_module *module;

module = container_of(chip, struct myart_gpio_module, gpio);
return module->intr_start + offset;

}

int myart_gpio_direction_input(struct gpio_chip *chip,unsigned offset)
{
myart_gpio_inout_pin(offset , MCPU_GPIO_INPUT);
return 0;
}

int myart_gpio_get_value(struct gpio_chip *chip, unsigned offset)
{
return myart_gpio_get_pin(offset);
}

int myart_gpio_direction_output(struct gpio_chip *chip,unsigned offset, int value)
{
myart_gpio_inout_pin(offset , MCPU_GPIO_OUTPUT);
return 0;
}

void myart_gpio_set_value(struct gpio_chip *chip, unsigned offset, int value)
{
myart_gpio_set_pin(offset , value);
}

static int __devinit myart_gpio_probe(struct platform_device *pdev)
{
……………………………….
/* Initialize the GPIO data structures */
module->gpio.dev = &pdev->dev;
module->gpio.label = pdev->name;
module->gpio.owner = THIS_MODULE;
module->gpio.direction_input = myart_gpio_direction_input;
module->gpio.get = myart_gpio_get_value;
module->gpio.direction_output = myart_gpio_direction_output;
module->gpio.set = myart_gpio_set_value;
module->gpio.to_irq = myart_gpio_to_irq;
module->gpio.can_sleep = 0;
module->gpio.base = 0;
module->gpio.ngpio = MCPU_GPIO_NUM;
ret = gpiochip_add(&(module->gpio));
……………………………………..
}

static int __devexit my_gpio_remove(struct platform_device *pdev)
{
……………………………..
ret = gpiochip_remove(&module->gpio);
if (ret) {
dev_err(dev, "unable to remove GPIO chip\n");
return ret;
}
………………………..
return 0;
}

static struct platform_driver my_gpio_driver = {
.driver = {
.name = "GPIO Driver",
.owner = THIS_MODULE,
},
.probe = my_gpio_probe,
.remove = __devexit_p(my_gpio_remove),
};

static int __init my_gpio_module_init(void)
{
return platform_driver_register(&my_gpio_driver);
}

static void __exit my_gpio_module_exit(void)
{
platform_driver_unregister(&my_gpio_driver);
}

module_init(my_gpio_module_init);
module_exit(my_gpio_module_exit);

上層GPIO應用驅動程式
上層的GPIO應用驅程式則是使用GPIO Kernel Library所提供的介面來撰寫驅動程式,如RTC, I2C等等都是常看到的使用GPIO來應用的驅動程式,它必須使用gpio_request_array()來向kernel取得所要用的GPIO,並確定它是没被其它驅動程式所使用,使用後並將其佔住不被其它驅動程式所佔用,在該應用驅動程式下載時需要呼叫gpio_free_array()將所佔用的GPIO free給其它的驅動程式使用,在整個驅動程式中則可以使用gpio_direction_input(), gpio_direction_output(), gpio_set_value(), gpio_get_value()來個別控制相對的GPIO為input or output,並且讀回或者設定相對GPIO為high or low。

程式範例
GPIO RTC驅動程式:
static void rtc_write(u8 cmd, u8 Data)
{
………………………
gpio_direction_output(GPIO_RTC_DATA, MCPU_GPIO_LOW);
    gpio_set_value(GPIO_RTC_RESET, MCPU_GPIO_HIGH);
………………………
}

static u8 rtc_read(u8 cmd)
{
…………………
gpio_direction_input(GPIO_RTC_DATA);
………………..
v = gpio_get_value(GPIO_RTC_DATA);
…………………….
}

static int myart_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
……………………..
v = rtc_read(RTC_MONTH_R);
………………………………
}

static int myart_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
……………………
rtc_write(RTC_YEAR_W, v);
……………………..
}

static const struct rtc_class_ops myart_rtc_ops = {
.read_time = myart_rtc_read_time,
.set_time = myart_rtc_set_time,
};

static struct gpio rtc_gpios[] = {
    { GPIO_RTC_RESET, GPIOF_DIR_OUT, "rtc_reset"}, 
    { GPIO_RTC_SCLK, GPIOF_DIR_OUT, "rtc_sclk"}, 
    { GPIO_RTC_DATA, GPIOF_DIR_OUT, "rtc_data"}, 
};

static int __init myart_rtc_probe(struct platform_device *pdev)
{
…………………………..
err = gpio_request_array(rtc_gpios, ARRAY_SIZE(rtc_gpios));
…………………………..
rtc = rtc_device_register("rtc-myart", &pdev->dev, &myart_rtc_ops,
  THIS_MODULE);
…………………………..
}

static int __exit myart_rtc_remove(struct platform_device *pdev)
{
…………………………..
rtc_device_unregister(rtc);
    gpio_free_array(rtc_gpios, ARRAY_SIZE(rtc_gpios)); 
return 0;
}

static struct platform_driver myart_rtc_driver = {
.driver = {
.name = "RTC", 
.owner = THIS_MODULE,
},
.remove = __exit_p(myart_rtc_remove),
};

static int __init myart_rtc_init(void)
{
return platform_driver_probe(&myart_rtc_driver, myart_rtc_probe);
}

static void __exit myart_rtc_exit(void)
{
    platform_driver_unregister(&myart_rtc_driver);
}

module_init(myart_rtc_init);
module_exit(myart_rtc_exit);

2012年3月19日 星期一

Linux Kernel New HAL Timer Device Driver


前言
由於現在的作業系統對於Real Time的需求越來越多,而對於Real Time的需求則是需要一個較精確的時間計時,以往的Linux在時間的設計上已經不符這個需求,所以在2.6.38之後的版本中已經針對它來修改,這份文件將會說明對於這個新架構的HAL Timer Device Driver的寫法為何。
以往的問題
  • 時間不夠精細,需要能夠取得nanosecond
  • 在計算時間時必需能夠考慮到timer counter overflow的問題
現在的做法
你可以註冊多個clocksource, 所謂的clocksource就是可以產生一個固定的clock, 並且可以讀回其計數值, 系統中將會比較各個clocksource的精確度, 以找出最佳的clocksource做為系統的clocksource, 而這個clocksource將會做為系統的計算時間用, 另外必須註冊一個做為clock event使用, 這就是取代以往的timer interrupt, 而clocksource和clockevent可以為同一個, 也可以為分別不同的timer, 若系統的timer夠多, 建議可以設定不同的timer
How to
  • 在arch/arm/mach-xxx的目錄中必須包以下的程式碼
    • 在MACHINE_START宣告中須有timer的資料結構指定
MACHINE_START(MOXAART, "Moxa ART CPU platform")
        .boot_params = 0x100,
        .fixup = moxaart_fixup,
        .map_io = moxaart_map_io, /* map physical to virtual address */
        .init_irq = irq_init_irq,
        .timer = &moxaart_timer,
        .init_machine = moxaart_init,  /* register  platform_device */
MACHINE_END
其中timer的資料結構如下
extern void     moxaart_timer_init(void);
static struct sys_timer moxaart_timer = {
        .init   = moxaart_timer_init,
};
其中init的成員為一個call back function,其寫法如下
void __init moxaart_timer_init(void)
{
        *timer_ctrl_reg = timer_ctrl_reg_value;
        setup_irq(IRQ_TIMER1, &moxaart_timer_irq);
        moxaart_clocksource_init();
        moxaart_clockevent_init();
}
請注意其中將會註冊一個clock source以及一個clock event。
  • 如何初始化以及設定一個clock source
    • 啓動該計時器,準備一個call back function讓系統可以隨時讀取讓計時器的值,最後呼叫一個kernel API clocksource_mmio_init()來設定一個clock source, 以下為範例程式
static cycle_t  moxaart_clocksource_read(struct clocksource *c)
{
        return ~moxaart_timer_get_counter(CLOCK_TIMER);
}
static void __init      moxaart_clocksource_init(void)
{
        moxaart_timer_set_reload(CLOCK_TIMER, MAX_COUNTER);
        moxaart_timer_set_counter(CLOCK_TIMER, MAX_COUNTER);
        moxaart_timer_enable(CLOCK_TIMER);
        clocksource_mmio_init(NULL, "timer2", APB_CLK, CLOCK_RATING, 32, moxaart_clocksource_read);
}
請注意在讀取計時器的值call back function中的return值,若讓計時器是遞減的值則必須反相加一個 ”~”, 若是遞加則不用。
  • 如何初始化以及設定一個clock event
    • 這個初始化較為繁複,首先你要準備一個資料結構struct clock_evelt_device, 以下為其範例
static struct clock_event_device        clockevent_moxaart = {
        .name           = "timer1",
        .features       = CLOCK_EVT_FEAT_PERIODIC|CLOCK_EVT_FEAT_ONESHOT,
        .irq            = IRQ_TIMER1,
        .rating         = CLOCK_RATING,
        .shift          = CLOCK_SHIFT,
        .set_mode       = moxaart_set_mode,
        .set_next_event = moxaart_set_next_event,
};
其中的features設定CLOCK_EVT_FEAT_PERIODIC, 是較為傳統的timer功能,就是會一直產生的固定時間,也就是以前所謂的tick功能,而CLOCK_EVT_FEAT_ONESHOT的功能較為新的設計,若要有high resolution (至nanosecond等級),則必須有這個功能,意思就是timer可以只產生一次到的interrupt,而不是前面所提的periodic一直產生的timer interrupt。另外對shift成員的設定為一個為2的幾次方值,如32則表示所有的timer counter值將會先乘於2的32次方。
    • 接下需要準備ISR, 其設定如下
static struct irqaction moxaart_timer_irq = {
        .name           = "timer1",
        .flags          = IRQF_DISABLED|IRQF_TIMER|IRQF_TRIGGER_FALLING|IRQF_IRQPOLL,
        .handler        = moxaart_timer_interrupt,
        .dev_id         = &clockevent_moxaart
};
請注意其成員dev_id為上面所宣告的clockevent_moxaart。
    • 在clock_event_device中的成員set_mode call back function則是在設定該timer是要使用何種方,以下為其寫法
static void     moxaart_set_mode(enum clock_event_mode mode, struct clock_event_device *evt)
{
        switch ( mode ) {
        case CLOCK_EVT_MODE_PERIODIC:
                moxaart_timer_set_reload(USED_TIMER, LATCH);
                moxaart_timer_set_counter(USED_TIMER, LATCH);
                moxaart_timer_enable(USED_TIMER);
                break;
        case CLOCK_EVT_MODE_ONESHOT:
                moxaart_timer_set_reload(USED_TIMER, 0);
                moxaart_timer_set_match1(USED_TIMER, 0);
                timer_ctrl_reg_value &= ~(TIMER1_INT_ENABLE);
                timer_ctrl_reg_value |= (TIMER1_ENABLE|TIMER1_USED_PCLK);
                *timer_ctrl_reg = timer_ctrl_reg_value;
                break;
        case CLOCK_EVT_MODE_RESUME:
                moxaart_timer_enable(USED_TIMER);
                break;
        case CLOCK_EVT_MODE_SHUTDOWN:
        case CLOCK_EVT_MODE_UNUSED:
        default:
                moxaart_timer_disable(USED_TIMER);
break;
        }
}
請注意其模式中的periodic和oneshot定義的不同,在oneshot的模式中會將reload的值設為零。
    • 而對於set_next_event的call back function,則是只是設定讓計時器的reload值
static int      moxaart_set_next_event(unsigned long evt, struct clock_event_device *unused)
{
        moxaart_timer_set_reload(USED_TIMER, evt);
        return 0;
}
    • 對於ISR的寫法如下
static irqreturn_t moxaart_timer_interrupt(int irq, void *dev_id)
{
        struct clock_event_device       *evt=dev_id;
        *(volatile unsigned int *)(MOXAART_TIMER_VA_BASE+TIMER_INTR_STATE) = 0;
        evt->event_handler(evt);
        return IRQ_HANDLED;
}
對於timer interrupt的寫法其實很制式,大家都一樣。
    • 最後則是clock event的初始化,其寫法如下
static void __init      moxaart_clockevent_init(void)
{
        clockevent_moxaart.mult = div_sc(APB_CLK, NSEC_PER_SEC, clockevent_moxaart.shift);
        clockevent_moxaart.max_delta_ns = clockevent_delta2ns(0xffffffff, &clockevent_moxaart);
        clockevent_moxaart.min_delta_ns = clockevent_delta2ns(0xf, &clockevent_moxaart);
        clockevent_moxaart.cpumask = cpumask_of(0);
        clockevents_register_device(&clockevent_moxaart);
}
  • 在arch/arm/Kconfig的有關自己CPU的選項中必須有以下的設定
select GENERIC_CLOCKEVENTS
select CLKSRC_MMIO
以下是一個範例:
config ARCH_MOXAART
        bool "MOXA ART CPU"
        select CPU_FA526
        select USB_ARCH_HAS_EHCI
        select MIGHT_HAVE_PCI
        select ARCH_REQUIRE_GPIOLIB
        select GENERIC_GPIO
        select GENERIC_CLOCKEVENTS
        select CLKSRC_MMIO
        help
          Support for MOXA ART CPU
附錄
Kernel APIs
/**
 * clocksource_mmio_init - Initialize a simple mmio based clocksource
 * @base:       Virtual address of the clock readout register
 * @name:       Name of the clocksource
 * @hz:         Frequency of the clocksource in Hz
 * @rating:     Rating of the clocksource
 * @bits:       Number of valid bits
 * @read:       One of clocksource_mmio_read*() above
 */
int __init clocksource_mmio_init(void __iomem *base, const char *name,
        unsigned long hz, int rating, unsigned bits,
        cycle_t (*read)(struct clocksource *))

2012年2月28日 星期二

API Select怎麼用


前言
常在寫網路程式的人對select()這個API應該不莫生才對,或常寫伺服器端程式也應該很熟悉才對,它可以同時監看多個檔案的讀寫允許,而且檔案包含各種週邊元件以及網路的socket,這裏我將會介紹它經常被用的應用,以及我個人經驗中覺有點特別的用法分享給其他人參考參考。
Select函式原型
int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds, struct timeval *restrict timeout);
這個函數主要是用來同時監看數個檔案(包含socket),其中的事件在一定時間內是否有發生,而其中的事件有是否有資料可取得,是否可以寫入,以及其它錯誤事件,若在一段時間內沒有任何事件產生,則會回覆一個timeout的訊息,這個函數好用在它可以同時監看很多個檔案,以及時間,如此一來可以讓電腦更有效率以及可以同時處理多個檔案。
其中輸入的參數說明如下:
int nfds
對所要監看的所有file handle中最大的file handle號碼加1
fd_set *restrict readfds
相對監看該檔案是否有資料已收到或者已有資料可讀,只是確認有資可讀,但不知有多少資料可讀。可以使用FD_SET來設定相對需要確認的檔案,以及使用FD_ISSET來檢查確認的結果。
fd_set *restrict writefds
相對確認該檔案是否可以寫入或者有空間可寫入,只是確認可寫入,但是有多少空間可寫入,並不知道。可以使用FD_SET來設定相對需要確認的檔案,以及使用FD_ISSET來檢查確認的結果。
fd_set *restrict errorfds
相對確認該檔案是否有任何錯誤產生,可以使用FD_SET來設定相對需要確認的檔案,以及使用FD_ISSET來檢查確認的結果。
struct timeval *restrict timeout
這是設定timeout時間,若為NULL則是無限制時間監看及確認,其宣告如下:
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
}
以上最小時間單位為usec,但是在Linux中系統tick設定通常為10ms之下,通常最小時間可細分也只能到10ms,比這個小的時間其實是無法達到的。
其回傳值如下:
> 0 表示所以監看的檔案中有幾個檔案已符合條件。
= 0 表示未任何事件發生並且已經timeout。
< 0 表示有錯誤產生。
其它會用到的巨集指令
void FD_CLR(int fd, fd_set *set);
清除掉該檔案相對檔案的設定,也就是取消對該檔案的監看。
int  FD_ISSET(int fd, fd_set *set);
檢查相對該檔案是否為真,就是對該檔案所監看的結果,確認是否為真,若是為真即表示該事件有發生,若為否則表示該事件未發生。
void FD_SET(int fd, fd_set *set);
設定監看該檔案行為。
void FD_ZERO(fd_set *set);
清除所有檔案的監看及設定。
正常使用
在Linux中任何週邊都是使用檔案來使用,所以在select中的所使用file handle可以是網路socket open來的,也可以是open device node所得來的,也可以是真正的檔案,所以以下為一個正常使用的範例:
int fd1=soekcet(…)
int fd2=open(“device-node-name”, …);
int maxfd;
fd_set readfds, writerfds;
struct timeval timev;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
if ( fd1 > fd2 )
maxfd = fd1;
else
maxfd = fd2;
FD_SET(fd1, &readfds);
FD_SET(fd2, &readfds);
FD_SET(fd1, &writefds);
FD_SET(fd2, &writefds);
timev.tv_sec = 3;
timev.tv_usec = 0;
if ( select(maxfd, &readfds, &writefds, NULL, &timev) ) {
if ( FD_ISSET(fd1, &readfds) ) {
// fd1有資料可讀
}
if ( FD_ISSET(fd2, &readfds) ) {
// fd2 有資料可讀
}
if ( FD_ISSET(fd1, &writefds) ) {
// fd1 可寫入
}
if (FD_ISSET(fd2, &writefds) ) {
// fd2 可寫入
}
} else {
// timeout or error 
}
以上的寫法在應用有許多好處,一是可以同時處理許多檔案,並且可以隨時加入或退出,二是使用事作觸發讓系統的負載不會過重,並且也可兼顧時效性。
短時間休眠用
前面所提是一般正常的使用方式,有一種另外的使用場合,那是因為在Linux的sleep function中只有秒級的單位,若我們想要實現msec的sleep則無法做到(注:現在在real time中可以有usleep),則可以利用select的timeout機制來作sleep的msec等級,其寫法如下:
struct timeval timev;
timev.tv_sec = 0;
timev.tv_usec = 10000; // 10msec
select(1, NULL, NULL, NULL, &timev);