亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
最近訪(fǎng)問(wèn)板塊 發(fā)新帖
查看: 1721 | 回復(fù): 0
打印 上一主題 下一主題

linux spi子系統(tǒng)驅(qū)動(dòng)分析1[轉(zhuǎn)] [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2010-02-02 13:38 |只看該作者 |倒序?yàn)g覽

2.6.18內(nèi)核下已經(jīng)添加了完整的spi子系統(tǒng)了,參考mtd的分析,將從下到上層,再?gòu)纳系较聦拥膶?duì)其進(jìn)行分析。
以下先從下到上的進(jìn)行分析:
driver/spi下有兩個(gè)底層相關(guān)的spi驅(qū)動(dòng)程序:
spi_s3c24xx.c和spi_s3c24xx_gpio.c
其中spi_s3c24xx.c是基于s3c24xx下相應(yīng)的spi接口的驅(qū)動(dòng)程序,spi_s3c24xx_gpio.c允許用戶(hù)指定3個(gè)gpio口,分別充當(dāng)spi_clk、spi_mosi和spi_miso接口,模擬標(biāo)準(zhǔn)的spi總線(xiàn)。
s3c2410自帶了兩個(gè)spi接口(spi0和spi1),在此我只研究基于s3c2410下spi接口的驅(qū)動(dòng)程序spi_s3c24xx.c。
首先從spi驅(qū)動(dòng)的檢測(cè)函數(shù)進(jìn)行分析:
static int s3c24xx_spi_probe(struct platform_device *pdev)
{
        struct s3c24xx_spi *hw;
        struct spi_master *master;
        struct spi_board_info *bi;
        struct resource *res;
        int err = 0;
        int i;
    /* pi_alloc_master函數(shù)申請(qǐng)了struct spi_master+struct s3c24xx_spi大小的數(shù)據(jù),
         * spi_master_get_devdata和pi_master_get分別取出struct s3c24xx_spi和struct spi_master結(jié)構(gòu)指針
          */
        master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));
        if (master == NULL) {
                dev_err(&pdev->dev, "No memory for spi_master\n");
                err = -ENOMEM;
                goto err_nomem;
         }
     /* 填充struct spi_master結(jié)構(gòu) */
        hw = spi_master_get_devdata(master);
        memset(hw, 0, sizeof(struct s3c24xx_spi));
        hw->master = spi_master_get(master);
        hw->pdata = pdev->dev.platform_data;
        hw->dev = &pdev->dev;
        if (hw->pdata == NULL) {
                dev_err(&pdev->dev, "No platform data supplied\n");
                err = -ENOENT;
                goto err_no_pdata;
        }
        platform_set_drvdata(pdev, hw);//dev_set_drvdata(&pdev->dev, hw)
        init_completion(&hw->done);
        /* setup the state for the bitbang driver */
   
     /* 填充hw->bitbang結(jié)構(gòu)(hw->bitbang結(jié)構(gòu)充當(dāng)一個(gè)中間層,相當(dāng)與input system的input_handle struct) */
        hw->bitbang.master         = hw->master;
        hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
        hw->bitbang.chipselect     = s3c24xx_spi_chipsel;
        hw->bitbang.txrx_bufs      = s3c24xx_spi_txrx;
        hw->bitbang.master->setup  = s3c24xx_spi_setup;
        dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);
        /* find and map our resources */
         /* 申請(qǐng)spi所用到的資源:io、irq、時(shí)鐘等 */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL) {
                dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
                err = -ENOENT;
                goto err_no_iores;
        }
        hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1,
                                        pdev->name);
        if (hw->ioarea == NULL) {
                dev_err(&pdev->dev, "Cannot reserve region\n");
                err = -ENXIO;
                goto err_no_iores;
        }
        hw->regs = ioremap(res->start, (res->end - res->start)+1);
        if (hw->regs == NULL) {
                dev_err(&pdev->dev, "Cannot map IO\n");
                err = -ENXIO;
                goto err_no_iomap;
        }
        hw->irq = platform_get_irq(pdev, 0);
        if (hw->irq dev, "No IRQ specified\n");
                err = -ENOENT;
                goto err_no_irq;
        }
        err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);
        if (err) {
                dev_err(&pdev->dev, "Cannot claim IRQ\n");
                goto err_no_irq;
        }
        hw->clk = clk_get(&pdev->dev, "spi");
        if (IS_ERR(hw->clk)) {
                dev_err(&pdev->dev, "No clock for device\n");
                err = PTR_ERR(hw->clk);
                goto err_no_clk;
        }
        /* for the moment, permanently enable the clock */
        clk_enable(hw->clk);
        /* program defaults into the registers */
         /* 初始化spi相關(guān)的寄存器 */
        writeb(0xff, hw->regs + S3C2410_SPPRE);
        writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);
        writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);
        /* add by lfc */
        s3c2410_gpio_setpin(S3C2410_GPE13, 0);
        s3c2410_gpio_setpin(S3C2410_GPE12, 0);
        s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0);
        s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0);
        s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0);
        /* end add */
        /* setup any gpio we can */
     /* 片選 */
        if (!hw->pdata->set_cs) {
                s3c2410_gpio_setpin(hw->pdata->pin_cs, 1);
                s3c2410_gpio_cfgpin(hw->pdata->pin_cs, S3C2410_GPIO_OUTPUT);
        }
        /* register our spi controller */
         /* 最終通過(guò)調(diào)用spi_register_master來(lái)注冊(cè)spi控制器(驅(qū)動(dòng)) */
        err = spi_bitbang_start(&hw->bitbang);
        if (err) {
                dev_err(&pdev->dev, "Failed to register SPI master\n");
                goto err_register;
        }
        dev_dbg(hw->dev, "shutdown=%d\n", hw->bitbang.shutdown);
        /* register all the devices associated */
         /* 注冊(cè)所用使用本spi驅(qū)動(dòng)的設(shè)備 */
        bi = &hw->pdata->board_info[0];
        for (i = 0; i pdata->board_size; i++, bi++) {
                dev_info(hw->dev, "registering %s\n", bi->modalias);
                bi->controller_data = hw;
                spi_new_device(master, bi);
        }
        return 0;
err_register:
        clk_disable(hw->clk);
        clk_put(hw->clk);
err_no_clk:
        free_irq(hw->irq, hw);
err_no_irq:
        iounmap(hw->regs);
err_no_iomap:
        release_resource(hw->ioarea);
        kfree(hw->ioarea);
err_no_iores:
err_no_pdata:
        spi_master_put(hw->master);;
err_nomem:
        return err;
}
/*
* spi_alloc_master - allocate SPI master controller
* @dev: the controller, possibly using the platform_bus
* @size: how much driver-private data to preallocate; the pointer to this
*      memory is in the class_data field of the returned class_device,
*      accessible with spi_master_get_devdata().
*
* This call is used only by SPI master controller drivers, which are the
* only ones directly touching chip registers.  It's how they allocate
* an spi_master structure, prior to calling spi_register_master().
*
* This must be called from context that can sleep.  It returns the SPI
* master structure on success, else NULL.
*
* The caller is responsible for assigning the bus number and initializing
* the master's methods before calling spi_register_master(); and (after errors
* adding the device) calling spi_master_put() to prevent a memory leak.
*/
/*注釋已經(jīng)寫(xiě)得很清楚了,本函數(shù)旨在分配spi_master struct
*其中,device為主控制設(shè)備,size為需要預(yù)分配的設(shè)備私有數(shù)據(jù)大小
*該函數(shù)被spi主控制器驅(qū)動(dòng)所調(diào)用,用于在調(diào)用spi_register_master注冊(cè)主控制器前
*分配spi_master struct,分配bus number和初始化主控制器的操作方法
*注意在分配spi_master struct的時(shí)候多分配了大小為size的設(shè)備私有數(shù)據(jù)
*并通過(guò)spi_master_set_devdata函數(shù)把其放到class_data field里,以后可以通過(guò)spi_master_get_devdata來(lái)訪(fǎng)問(wèn)
*/
struct spi_master * __init_or_module
spi_alloc_master(struct device *dev, unsigned size)
{
        struct spi_master       *master;
        if (!dev)
                return NULL;
        master = kzalloc(size + sizeof *master, SLAB_KERNEL);
        if (!master)
                return NULL;
        class_device_initialize(&master->cdev);
        master->cdev.class = &spi_master_class;
        master->cdev.dev = get_device(dev);
        spi_master_set_devdata(master, &master[1]);
        return master;
}
/*
* spi_bitbang_start - start up a polled/bitbanging SPI master driver
* @bitbang: driver handle
*
* Caller should have zero-initialized all parts of the structure, and then
* provided callbacks for chip selection and I/O loops.  If the master has
* a transfer method, its final step should call spi_bitbang_transfer; or,
* that's the default if the transfer routine is not initialized.  It should
* also set up the bus number and number of chipselects.
*
* For i/o loops, provide callbacks either per-word (for bitbanging, or for
* hardware that basically exposes a shift register) or per-spi_transfer
* (which takes better advantage of hardware like fifos or DMA engines).
*
* Drivers using per-word I/O loops should use (or call) spi_bitbang_setup and
* spi_bitbang_cleanup to handle those spi master methods.  Those methods are
* the defaults if the bitbang->txrx_bufs routine isn't initialized.
*
* This routine registers the spi_master, which will process requests in a
* dedicated task, keeping IRQs unblocked most of the time.  To stop
* processing those requests, call spi_bitbang_stop().
*/
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
        int     status;
        if (!bitbang->master || !bitbang->chipselect)
                return -EINVAL;
     /*bitbang_work
       * 初始化a work,后面再create_singlethread_workqueue,
      * 等到有數(shù)據(jù)要傳輸?shù)臅r(shí)候,在spi_bitbang_transfer函數(shù)中通過(guò)調(diào)用queue_work(bitbang->workqueue, &bitbang->work)
      * 把work扔進(jìn)workqueue中調(diào)度運(yùn)行
      * 這是內(nèi)核的一貫做法,在mmc/sd驅(qū)動(dòng)中也是這樣處理的^_^
      */
        INIT_WORK(&bitbang->work, bitbang_work, bitbang);
     /* 初始化自旋鎖和鏈表頭,以后用到 */
        spin_lock_init(&bitbang->lock);
     spi_new_device   INIT_LIST_HEAD(&bitbang->queue);
        if (!bitbang->master->transfer)
                bitbang->master->transfer = spi_bitbang_transfer;//spi數(shù)據(jù)的傳輸就是通過(guò)調(diào)用這個(gè)方法來(lái)實(shí)現(xiàn)的
     /* spi_s3c24xx.c驅(qū)動(dòng)中有相應(yīng)的txrx_bufs處理方法,在bitbang_work中被調(diào)用 */
        if (!bitbang->txrx_bufs) {
                bitbang->use_dma = 0;
                bitbang->txrx_bufs = spi_bitbang_bufs;
                if (!bitbang->master->setup) {
                        if (!bitbang->setup_transfer)
                                bitbang->setup_transfer =
                                         spi_bitbang_setup_transfer;
                        bitbang->master->setup = spi_bitbang_setup;
                        bitbang->master->cleanup = spi_bitbang_cleanup;
                }
     /* spi_s3c24xx.c驅(qū)動(dòng)中有相應(yīng)的setup處理方法,在稍后的spi_new_device中被調(diào)用 */
        } else if (!bitbang->master->setup)
                return -EINVAL;
        /* this task is the only thing to touch the SPI bits */
        bitbang->busy = 0;
     /* 創(chuàng)建工作者進(jìn)程 */
        bitbang->workqueue = create_singlethread_workqueue(
                        bitbang->master->cdev.dev->bus_id);
        if (bitbang->workqueue == NULL) {
                status = -EBUSY;
                goto err1;
        }
        /* driver may get busy before register() returns, especially
         * if someone registered boardinfo for devices
         */
        status = spi_register_master(bitbang->master);
        if (status workqueue);
err1:
        return status;
}
/**
* spi_register_master - register SPI master controller
* @master: initialized master, originally from spi_alloc_master()
*
* SPI master controllers connect to their drivers using some non-SPI bus,
* such as the platform bus.  The final stage of probe() in that code
* includes calling spi_register_master() to hook up to this SPI bus glue.
*
* SPI controllers use board specific (often SOC specific) bus numbers,
* and board-specific addressing for SPI devices combines those numbers
* with chip select numbers.  Since SPI does not directly support dynamic
* device identification, boards need configuration tables telling which
* chip is at which address.
*
* This must be called from context that can sleep.  It returns zero on
* success, else a negative error code (dropping the master's refcount).
* After a successful return, the caller is responsible for calling
* spi_unregister_master().
*/
int __init_or_module
spi_register_master(struct spi_master *master)
{
        static atomic_t         dyn_bus_id = ATOMIC_INIT((1cdev.dev;
        int                     status = -ENODEV;
        int                     dynamic = 0;
        if (!dev)
                return -ENODEV;
        /* convention:  dynamically assigned bus IDs count down from the max */
        if (master->bus_num bus_num = atomic_dec_return(&dyn_bus_id);
                dynamic = 1;
        }
        /* register the device, then userspace will see it.
         * registration fails if the bus ID is in use.
         */
        snprintf(master->cdev.class_id, sizeof master->cdev.class_id,
                "spi%u", master->bus_num);
        status = class_device_add(&master->cdev);//注冊(cè)設(shè)備
        if (status cdev.class_id,
                        dynamic ? " (dynamic)" : "");
        /* populate children from any spi device tables */
        scan_boardinfo(master);
        status = 0;
done:
        return status;
}
/* FIXME someone should add support for a __setup("spi", ...) that
* creates board info from kernel command lines
*/
/*
* scan board_list for spi_board_info which is registered by spi_register_board_info
* 很可惜,s3c24xx的spi驅(qū)動(dòng)中不支持spi_register_board_info這種標(biāo)準(zhǔn)方式注冊(cè)方式,而是直接調(diào)用spi_new_device內(nèi)部函數(shù)
*/
static void __init_or_module
scan_boardinfo(struct spi_master *master)
{
        struct boardinfo        *bi;
        struct device           *dev = master->cdev.dev;
        down(&board_lock);
        list_for_each_entry(bi, &board_list, list) {
                struct spi_board_info   *chip = bi->board_info;
                unsigned                n;
                for (n = bi->n_board_info; n > 0; n--, chip++) {
                        if (chip->bus_num != master->bus_num)
                                continue;
                        /* some controllers only have one chip, so they
                         * might not use chipselects.  otherwise, the
                         * chipselects are numbered 0..max.
                         */
                        if (chip->chip_select >= master->num_chipselect
                                        && master->num_chipselect) {
                                dev_dbg(dev, "cs%d > max %d\n",
                                        chip->chip_select,
                                        master->num_chipselect);
                                continue;
                        }
                        (void) spi_new_device(master, chip);
                }
        }
        up(&board_lock);
}
/*
* Board-specific early init code calls this (probably during arch_initcall)
* with segments of the SPI device table.  Any device nodes are created later,
* after the relevant parent SPI controller (bus_num) is defined.  We keep
* this table of devices forever, so that reloading a controller driver will
* not make Linux forget about these hard-wired devices.
*
* Other code can also call this, e.g. a particular add-on board might provide
* SPI devices through its expansion connector, so code initializing that board
* would naturally declare its SPI devices.
*
* The board info passed can safely be __initdata ... but be careful of
* any embedded pointers (platform_data, etc), they're copied as-is.
*/
int __init
spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
        struct boardinfo        *bi;
        bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);
        if (!bi)
                return -ENOMEM;
        bi->n_board_info = n;
        memcpy(bi->board_info, info, n * sizeof *info);
        down(&board_lock);
        list_add_tail(&bi->list, &board_list);
        up(&board_lock);
        return 0;
}
/* On typical mainboards, this is purely internal; and it's not needed
* after board init creates the hard-wired devices.  Some development
* platforms may not be able to use spi_register_board_info though, and
* this is exported so that for example a USB or parport based adapter
* driver could add devices (which it would learn about out-of-band).
*/
struct spi_device *__init_or_module
spi_new_device(struct spi_master *master, struct spi_board_info *chip)
{
        struct spi_device       *proxy;//這個(gè)結(jié)構(gòu)很重要,以后就是通過(guò)這個(gè)結(jié)構(gòu)來(lái)操作實(shí)際的spi設(shè)備的
        struct device           *dev = master->cdev.dev;
        int                     status;
        /* NOTE:  caller did any chip->bus_num checks necessary */
        if (!spi_master_get(master))
                return NULL;
        proxy = kzalloc(sizeof *proxy, GFP_KERNEL);
        if (!proxy) {
                dev_err(dev, "can't alloc dev for cs%d\n",
                        chip->chip_select);
                goto fail;
         }
     /* 初始化spi_device 結(jié)構(gòu)各成員 */
        proxy->master = master;
        proxy->chip_select = chip->chip_select;
        proxy->max_speed_hz = chip->max_speed_hz;
        proxy->mode = chip->mode;
        proxy->irq = chip->irq;
        proxy->modalias = chip->modalias;
        snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,
                        "%s.%u", master->cdev.class_id,
                        chip->chip_select);
        proxy->dev.parent = dev;
        proxy->dev.bus = &spi_bus_type;
        proxy->dev.platform_data = (void *) chip->platform_data;
        proxy->controller_data = chip->controller_data;
        proxy->controller_state = NULL;
        proxy->dev.release = spidev_release;
        /* drivers may modify this default i/o setup */
     /* 調(diào)用master->setup(即s3c24xx_spi_setup)函數(shù)初始化spi設(shè)備 */
        status = master->setup(proxy);
        if (status dev.bus_id, status);
                goto fail;
        }
        /* driver core catches callers that misbehave by defining
         * devices that already exist.
         */
        status = device_register(&proxy->dev);//真正注冊(cè)原始設(shè)備
        if (status dev.bus_id, status);
                goto fail;
        }
        dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id);
        return proxy;
fail:
        spi_master_put(master);
        kfree(proxy);
        return NULL;
}
static int s3c24xx_spi_setup(struct spi_device *spi)
{
        int ret;
     /* 進(jìn)行一些檢查性操作 */
        if (!spi->bits_per_word)
                spi->bits_per_word = 8;
        if ((spi->mode & SPI_LSB_FIRST) != 0)
                return -EINVAL;
        ret = s3c24xx_spi_setupxfer(spi, NULL);
        if (ret dev, "setupxfer returned %d\n", ret);
                return ret;
        }
        dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n",
                __FUNCTION__, spi->mode, spi->bits_per_word,
                spi->max_speed_hz);
        return 0;
}
static int s3c24xx_spi_setupxfer(struct spi_device *spi,
                                 struct spi_transfer *t)
{
        struct s3c24xx_spi *hw = to_hw(spi);
        unsigned int bpw;
        unsigned int hz;
        unsigned int div;
        bpw = t ? t->bits_per_word : spi->bits_per_word;
        hz  = t ? t->speed_hz : spi->max_speed_hz;
        if (bpw != 8) {
                dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);
                return -EINVAL;
        }
        div = clk_get_rate(hw->clk) / hz;
        /* is clk = pclk / (2 * (pre+1)), or is it
         *    clk = (pclk * 2) / ( pre + 1) */
        div = (div / 2) - 1;//求出預(yù)分頻值
        if (div  255)
                div = 255;
        dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", div, hz);
        writeb(div, hw->regs + S3C2410_SPPRE);//設(shè)置預(yù)分頻值
        spin_lock(&hw->bitbang.lock);
        if (!hw->bitbang.busy) {
                hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);//修改時(shí)鐘,先禁用spi
                /* need to ndelay for 0.5 clocktick ? */
        }
        spin_unlock(&hw->bitbang.lock);
        return 0;
}
static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
{
        struct s3c24xx_spi *hw = to_hw(spi);
        unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
        unsigned int spcon;
        switch (value) {
        case BITBANG_CS_INACTIVE:
     /* 禁用spi(禁用片選) */
                if (hw->pdata->set_cs)
                        hw->pdata->set_cs(hw->pdata, value, cspol);
                else
                        s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol ^ 1);
                break;
        case BITBANG_CS_ACTIVE:
    /*
     * 啟用spi:根據(jù)需要設(shè)置寄存器并啟用使能片選
     * (如果spi_board_info中沒(méi)有設(shè)置相應(yīng)的mode選項(xiàng)的話(huà),那就只能使用默認(rèn)值SPPIN_DEFAULT和SPCON_DEFAULT了)
     */
                spcon = readb(hw->regs + S3C2410_SPCON);
                if (spi->mode & SPI_CPHA)
                        spcon |= S3C2410_SPCON_CPHA_FMTB;
                else
                        spcon &= ~S3C2410_SPCON_CPHA_FMTB;
                if (spi->mode & SPI_CPOL)
                        spcon |= S3C2410_SPCON_CPOL_HIGH;
                else
                        spcon &= ~S3C2410_SPCON_CPOL_HIGH;
                spcon |= S3C2410_SPCON_ENSCK;
                /* write new configration */
                writeb(spcon, hw->regs + S3C2410_SPCON);
                if (hw->pdata->set_cs)
                        hw->pdata->set_cs(hw->pdata, value, cspol);
                else
                        s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol);
                break;
        }
}
好了,至此spi主控制器(驅(qū)動(dòng))和板上spi設(shè)備注冊(cè)完畢,以后要使用spi來(lái)傳輸數(shù)據(jù)的話(huà),只要先獲得spi設(shè)備結(jié)構(gòu),然后就可以利用它來(lái)和spi驅(qū)動(dòng)打交道了(就好像你要操作一個(gè)文件,先要獲取文件句柄一樣,明白吧^_^)

本文來(lái)自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u3/110644/showart_2167356.html
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專(zhuān)區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過(guò)ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP