娇小w搡bbbb搡bbb,《第一次の人妻》,中国成熟妇女毛茸茸,边啃奶头边躁狠狠躁视频免费观看

【IMX6ULL學習筆記】十、Linux字符設備驅動

發布者:HarmoniousDream最新更新時間:2025-03-03 來源: cnblogs關鍵字:Linux  字符設備驅動 手機看文章 掃描二維碼
隨時隨地手機看文章

一、字符設備驅動簡介

Linux 應用程序對驅動程序的調用如圖所示:

image

在 Linux 中一切皆為文件,驅動加載成功以后會在“/dev”目錄下生成一個相應的文件,應用程序通過對這個名為“/dev/xxx”(xxx 是具體的驅動文件名字)的文件進行相應的操作即可實現對硬件的操作。

比如有個叫做 /dev/led 的驅動文件,此文件是 led 燈的驅動文件。應用程序使用 open 函數來打開文件/dev/led,使用完后使用 close 函數關閉 /dev/led 這個文件。open和close就是打開和關閉 led 驅動的函數,如果要點亮或關閉led,那么就使用 write 函數來操作,也就是向此驅動寫入數據,這個數據就是要關閉還是要打開 led 的控制參數。如果要獲取 led 燈的狀態,就用 read 函數從驅動中讀取相應的狀態。


應用程序運行在用戶空間,而 Linux 驅動屬于內核的一部分,因此驅動運行于內核空間。在用戶空間想要實現對內核的操作,如使用 open 函數打開 /dev/led 這個驅動,因為用戶空間不能直接對內核進行操作,因此必須使用一個叫做“系統調用”的方法來實現從用戶空間“陷入”到內核空間,這樣才能實現對底層驅動的操作。

open、close、write 和 read 等這些函數是由 C 庫提供的,在 Linux 系統中,系統調用作為 C 庫的一部分。當我們調用 open 函數的時候流程如圖 40.1.2 所示:

image

應用程序使用到的函數在具體驅動程序中都有與之對應的函數,比如應用程序中調用了 open 這個函數,那么在驅動程序中也得有一個名為 open 的函數。

每一個系統調用,在驅動中都有與之對應的一個驅動函數,在 Linux 內核文件 include/linux/fs.h 中有個叫做 file_operations 的結構體,此結構體就是 Linux 內核驅動操作函數集合,內容如下:


struct file_operations {

struct module *owner;

loff_t (*llseek) (struct file *, loff_t, int);

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);

ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);

int (*iterate) (struct file *, struct dir_context *);

unsigned int (*poll) (struct file *, struct poll_table_struct *);

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

int (*mmap) (struct file *, struct vm_area_struct *);

int (*mremap)(struct file *, struct vm_area_struct *);

int (*open) (struct inode *, struct file *);

int (*flush) (struct file *, fl_owner_t id);

int (*release) (struct inode *, struct file *);

int (*fsync) (struct file *, loff_t, loff_t, int datasync);

int (*aio_fsync) (struct kiocb *, int datasync);

int (*fasync) (int, struct file *, int);

int (*lock) (struct file *, int, struct file_lock *);

ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

int (*check_flags)(int);

int (*flock) (struct file *, int, struct file_lock *);

ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);

ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);

int (*setlease)(struct file *, long, struct file_lock **, void **);

long (*fallocate)(struct file *file, int mode, loff_t offset,

  loff_t len);

void (*show_fdinfo)(struct seq_file *m, struct file *f);

#ifndef CONFIG_MMU

unsigned (*mmap_capabilities)(struct file *);

#endif

};

比較重要、常用的函數:

第 2 行:owner 擁有該結構體的模塊的指針,一般設置為 THIS_MODULE。

第 3 行:llseek 函數用于修改文件當前的讀寫位置。

第 4 行:read 函數用于讀取設備文件。

第 5 行:write 函數用于向設備文件寫入(發送)數據。

第 9 行:poll 是個輪詢函數,用于查詢設備是否可以進行非阻塞的讀寫。

第 10 行:unlocked_ioctl 函數提供對于設備的控制功能,與應用程序中的ioctl函數對應。

第 11 行:compat_ioctl 函數與 unlocked_ioctl 函數功能一樣,區別在于在 64 位系統上,32 位的應用程序調用將會使用此函數。在 32 位的系統上運行 32 位的應用程序調用的是 unlocked_ioctl。

第 12 行:mmap 函數用于將設備的內存映射到進程空間中(也就是用戶空間),一般幀緩沖設備會使用此函數,比如 LCD 驅動的顯存,將幀緩沖(LCD 顯存)映射到用戶空間中以后應用程序就可以直接操作顯存了,就不用在用戶空間和內核空間之間來回復制。

第 14 行:open 函數用于打開設備文件。

第 16 行:release 用于釋放(關閉)設備文件,與應用程序中 close 函數對應。

第 17 行:fasync 函數用于刷新待處理的數據,用于將緩沖區中的數據刷新到磁盤。

第 18 行:aio_fsync 函數與 fasync 函數的功能類似,只是 aio_fsync 是異步刷新待處理的數據。


二、字符設備驅動開發步驟

2.1 驅動模塊的加載和卸載

Linux 驅動有兩種運行方式,第一種就是將驅動編譯進 Linux 內核中,這樣當 Linux 內核啟動的時候就會自動運行驅動程序。第二種就是將驅動編譯成模塊(Linux 下模塊擴展名為.ko),在 Linux 內核啟動以后使用“insmod”命令加載驅動模塊。

模塊有加載和卸載兩種操作,我們在編寫驅動的時候需要注冊這兩種操作函數,模塊的加載和卸載注冊函數如下:


module_init(xxx_init);   //注冊模塊加載函數

module_exit(xxx_exit);   //注冊模塊卸載函數

module_init 函數用來向 Linux 內核注冊一個模塊加載函數,參數 xxx_init 就是需要注冊的具體函數,當使用“insmod”命令加載驅動的時候,xxx_init 這個函數就會被調用。

module_exit 函數用來向 Linux 內核注冊一個模塊卸載函數,參數 xxx_exit 就是需要注冊的具體函數,當使用“rmmod”命令卸載具體驅動的時候 xxx_exit 函數就會被調用。

字符設備驅動模塊加載和卸載模板如下所示:


/* 驅動入口函數 */

static int __init xxx_init(void)

{

    /* 入口函數具體內容 */

    return 0;

}


/* 驅動出口函數 */

static void __exit xxx_exit(void)

{

    /* 出口函數具體內容 */

}


/* 將上面兩個函數指定為驅動的入口和出口函數 */

module_init(xxx_init);

module_exit(xxx_exit);

第 2 行:定義了個名為 xxx_init 的驅動入口函數,并且使用了“__init”來修飾。

第 9 行:定義了個名為 xxx_exit 的驅動出口函數,并且使用了“__exit”來修飾。

第 15 行:調用函數 module_init 來聲明 xxx_init 為驅動入口函數,當加載驅動的時候 xxx_init 函數就會被調用。

第 16 行:調用函數module_exit來聲明xxx_exit為驅動出口函數,當卸載驅動的時候xxx_exit 函數就會被調用。


驅動編譯完成以后擴展名為.ko,有兩種命令可以加載驅動模塊:insmod 和 modprobe。

insmod 是最簡單的模塊加載命令,此命令用于加載指定的.ko 模塊,比如加載 drv.ko 這個驅動模塊,命令如下:


insmod drv.ko

insmod 命令不能解決模塊的依賴關系,比如 drv.ko 依賴 first.ko 這個模塊,必須先使用 insmod 命令加載 first.ko 模塊,然后再加載 drv.ko 這個模塊。

modprobe 命令會分析模塊的依賴關系,然后會將所有的依賴模塊都加載到內核中。modprobe 命令主要智能在提供了模塊的依賴性分析、錯誤檢查、錯誤報告等功能。modprobe 命令默認會去 /lib/modules/ 目錄中查找模塊,比如使用的 Linux kernel 的版本號為 4.1.15,modprobe 命令默認會到 /lib/modules/4.1.15 這個目錄中查找相應的驅動模塊,一般自己制作的根文件系統中是不會有這個目錄的,需要手動創建。


驅動模塊的卸載使用命令“rmmod”即可,比如要卸載 drv.ko,使用如下命令:


rmmod drv.ko

也可以使用“modprobe -r”命令卸載驅動,比如要卸載 drv.ko,命令如下:


modprobe -r drv.ko

modprobe 命令可以卸載掉驅動模塊所依賴的其他模塊,前提是這些依賴模塊已經沒有被其他模塊所使用,否則就不能使用 modprobe 來卸載驅動模塊。所以對于模塊的卸載,推薦使用 rmmod 命令。


2.2 字符設備注冊與注銷

對于字符設備驅動而言,當驅動模塊加載成功以后需要注冊字符設備,同樣,卸載驅動模塊的時候也需要注銷掉字符設備。字符設備的注冊和注銷函數原型如下所示:


static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)

static inline void unregister_chrdev(unsigned int major, const char *name)

register_chrdev 函數用于注冊字符設備,此函數一共有三個參數,這三個參數的含義如下:


major:主設備號,Linux 下每個設備都有一個設備號,分為主設備號和次設備號。

name:設備名字,指向一串字符串。

fops:結構體 file_operations 類型指針,指向設備的操作函數集合變量。

unregister_chrdev 函數用于注銷字符設備,此函數有兩個參數,這兩個參數含義如下:


major:要注銷的設備對應的主設備號。

name:要注銷的設備對應的設備名。

一般字符設備的注冊在驅動模塊的入口函數 xxx_init 中進行,字符設備的注銷在驅動模塊的出口函數 xxx_exit 中進行。字符設備的注冊和注銷示例代碼如下所示:


static struct file_operations test_fops;


/* 驅動入口函數 */

static int __init xxx_init(void)

{

    /* 入口函數具體內容 */

    int retvalue = 0;

    /* 注冊字符設備驅動 */

    retvalue = register_chrdev(200, 'chrtest', &test_fops);

    if(retvalue < 0){

        /* 字符設備注冊失敗,自行處理 */

    }

    return 0;

}


/* 驅動出口函數 */

static void __exit xxx_exit(void)

{

    /* 注銷字符設備驅動 */

    unregister_chrdev(200, 'chrtest');

}


/* 將上面兩個函數指定為驅動的入口和出口函數 */

module_init(xxx_init);

module_exit(xxx_exit);

2.3 實現設備的具體操作函數

1、對 chrtest 進行打開和關閉操作

設備打開和關閉是最基本的要求,幾乎所有的設備都得提供打開和關閉的功能。因此我們需要實現 file_operations 中的 open 和 release 這兩個函數。


2、對 chrtest 進行讀寫操作

假設 chrtest 設備控制著一段緩沖區(內存),應用程序通過 read 和 write 這兩個函數對 chrtest 的緩沖區進行讀寫操作,需要實現 file_operations 中的 read 和 write 這兩個函數。

加入 file_operations 結構體變量 test_fops 的初始化操,內容如下:


/* 打開設備 */

static int chrtest_open(struct inode *inode, struct file *filp)

{

    /* 用戶實現具體功能 */

    return 0;

}


/* 從設備讀取 */

static ssize_t chrtest_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)

{

    /* 用戶實現具體功能 */

    return 0;

}


/* 向設備寫數據 */

static ssize_t chrtest_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)

{

    /* 用戶實現具體功能 */

    return 0;

}


/* 關閉/釋放設備 */

static int chrtest_release(struct inode *inode, struct file *filp)

{

    /* 用戶實現具體功能 */

    return 0;

}


static struct file_operations test_fops = {

    .owner = THIS_MODULE,

    .open = chrtest_open,

    .read = chrtest_read,

    .write = chrtest_write,

    .release = chrtest_release,

};


/* 驅動入口函數 */

static int __init xxx_init(void)

{

    /* 入口函數具體內容 */

    int retvalue = 0;

    /* 注冊字符設備驅動 */

    retvalue = register_chrdev(200, 'chrtest', &test_fops);

    if(retvalue < 0){

        /* 字符設備注冊失敗,自行處理 */

    }

    return 0;

}


/* 驅動出口函數 */

static void __exit xxx_exit(void)

{

    /* 注銷字符設備驅動 */

    unregister_chrdev(200, 'chrtest');

}


/* 將上面兩個函數指定為驅動的入口和出口函數 */

module_init(xxx_init);

module_exit(xxx_exit);

2.4 添加 LICENSE 和作者信息

LICENSE 和作者信息的添加使用如下兩個函數:


MODULE_LICENSE() //添加模塊 LICENSE 信息

MODULE_AUTHOR() //添加模塊作者信息

三、Linux 設備號

3.1 設備號的組成

Linux 中每個設備都有一個設備號,設備號由主設備號和次設備號兩部分組成,主設備號表示某一個具體的驅動,次設備號表示使用這個驅動的各個設備。Linux 提供了

一個 dev_t 的數據類型表示設備號,dev_t 定義在include/linux/types.h 里面,定義如下:


typedef __u32 __kernel_dev_t;

......

typedef __kernel_dev_t dev_t;

其中__u32 定義在文件 include/uapi/asm-generic/int-ll64.h 里面:


typedef unsigned int __u32;

因此 dev_t 是 unsigned int 類型,是一個 32 位的數據類型。這 32 位的數據構成了主設備號和次設備號,其中高 12 位為主設備號,低 20 位為次設備號。


在文件 include/linux/kdev_t.h 中提供了幾個關于設備號的操作函數(本質是宏),如下所示:


#define MINORBITS 20

#define MINORMASK ((1U << MINORBITS) - 1)


#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))

#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))

#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

第 1 行:宏 MINORBITS 表示次設備號位數,一共是 20 位。

第 2 行:宏 MINORMASK 表示次設備號掩碼。

第 3 行:宏 MAJOR 用于從 dev_t 中獲取主設備號,將 dev_t 右移 20 位即可。

第 4 行:宏 MINOR 用于從 dev_t 中獲取次設備號,取 dev_t 的低 20 位的值。

第 6 行:宏 MKDEV 用于將給定的主設備號和次設備號合成 dev_t 類型的設備號。


3.2 設備號的分配

1、靜態分配設備號

注冊字符設備的時候需要給設備指定一個設備號,這個設備號可以是驅動開發者靜態的指定一個設備號,比如選擇 200 這個主設備號。

靜態分配設備號需要我們檢查當前系統中所有被使用了的設備號,然后挑選一個沒有使用的。


2、動態分配設備號

在注冊字符設備之前先申請一個設備號,系統會自動給你一個沒有被使用的設備號,卸載驅動的時候釋放掉這個設備號即可。

設備號的申請函數如下:


int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

函數 alloc_chrdev_region 用于申請設備號,此函數有 4 個參數:


dev:保存申請到的設備號。

baseminor:次設備號起始地址,alloc_chrdev_region 可以申請一段連續的多個設備號,這些設備號的主設備號一樣,但是次設備號不同,次設備號以 baseminor 為起始地址地址開始遞增。一般 baseminor 為 0,也就是說次設備號從 0 開始。

count:要申請的設備號數量。

name:設備名字。

注銷字符設備之后要釋放掉設備號,設備號釋放函數如下:


void unregister_chrdev_region(dev_t from, unsigned count)

此函數有兩個參數:


from:要釋放的設備號。

count:表示從 from 開始,要釋放的設備號數量。

四、工程創建和模板

4.1、添加頭文件路徑

編寫 Linux 驅動,因此會用到 Linux 源碼中的函數。我們需要在 VSCode 中添加 Linux 源碼中的頭文件路徑。打開 VSCode,按下“Crtl+Shift+P”打開 VSCode 的控制臺,然后輸入 “C/C++: Edit configurations(JSON) ”,打開 C/C++編輯配置文件,如圖 40.4.1.3 所示:

image

打開以后會自動在.vscode 目錄下生成一個名為 c_cpp_properties.json 的文件,此文件默認內容如下所示:


{

    'configurations': [

        {

            'name': 'Linux',

            'includePath': [

                '${workspaceFolder}/**',

            ],

            'defines': [],

            'compilerPath': '/usr/bin/clang',

[1] [2]
關鍵字:Linux  字符設備驅動 引用地址:【IMX6ULL學習筆記】十、Linux字符設備驅動

上一篇:【IMX6ULL學習筆記】十一、LED字符設備
下一篇:【IMX6ULL學習筆記】九、Linux內核移植

推薦閱讀最新更新時間:2025-05-10 15:19

迅為4412開發板Linux字符設備控制(二)
17.3 字符類 Buzzer 蜂鳴器 和 led 燈類似,蜂鳴器的設備節點也是在/dev 目錄下,如下圖所示。 蜂鳴器的硬件和 led 燈類似,如下圖所示。 如上圖所示。 原理圖很容易理解,如果網絡 MOTOR_ PWM 為高電平,則 L9014 導通,蜂鳴器響,如果網絡MOTOR_PWM 為低電平,則 L9014 截止,蜂鳴器則不響。 操作方式和 led 小燈類似。 蜂鳴器測試例程 編寫簡單的 buzzertest.c 文件測試蜂鳴器。 首先添加頭文件,如下圖所示,下面新加了幾個庫文件,一般常用的就是下面幾個,寫代碼的時候, 為了方便,可以直接都添加上。 然后 main 函數如下圖所示。 如上圖
[單片機]
迅為4412開發板<font color='red'>Linux</font><font color='red'>字符</font>設備控制(二)
S3C6410嵌入式應用平臺構建(六)——linux-3.14.4移植到OK6410-(Yaffs2文件制作)
本文主要講怎用利用yaffs2工具和busybox制作yaffs2文件系統鏡像。大多數都是參照網上的,目的在于記錄學習,不做任何用途。 一、制作mkyaffs2image工具 進入yaffs2源碼目錄下utils目錄,修改mkyaff2image.c,做如下修改: // Adjust these to match your NAND LAYOUT: #if 0 #define chunkSize 2048 #define spareSize 64 #define pagesPerBlock 64 #else #define chunkSize 4096 #define spareSize 218 #defin
[單片機]
Linux內核源碼閱讀記錄一之分析存儲在不同段中的函數調用過程
在寫驅動的過程中,對于入口函數與出口函數我們會用一句話來修飾他們:module_init與module_exit,那會什么經過修飾后,內核就能狗調用我們編寫的入口函數與出口函數呢?下面就來分析內核調用module_init的過程(這里暫時分析編譯進內核的模塊,不涉及動態加載的模塊),以這個過程為例子來了解內核對于不同段的函數的調用過程。 下面從內核的start_kernel函數開始分析,下面是調用過程: start_kernel rest_init kernel_init do_basic_setup do_initcalls() 直接看到do_initcalls函數,看到第6行的f
[單片機]
Linux Platform devices 平臺設備驅動
platform平臺設備驅動是基于設備總線驅動模型的,它只不過是將 device 進一步封裝成為 platform_device,將 device_driver 進一步封裝成為 platform_device_driver,前面已經分析過設備總線驅動模型,關于device 與 device_driver 的注冊過程以及它們在sysfs文件系統中的層次關系就不在分析,本文重點分析platform平臺設備驅動與設備總線驅動模型相比較新增添的那些東西。 在Linux設備模型的抽象中,存在著一類稱作“Platform Device”的設備,內核是這樣描述它們的(Documentation/driver-model/platform.t
[單片機]
linux-2.6.32在mini2440開發板上移植-RTC移植
激活RTC 驅動 編者:RTC時鐘在S3C2440上的移植非常的簡單,因為linux已經支持了,仍以platform的形式來實現,只要把RTC的platform_deivce進行注冊,對內核進行簡單配置就好。但對于RTC的驅動的實現,很有值得研究的地方。這一驅動典型的使用了,linux內核中驅動分層設計的思想,對這驅動的研究,有利于理解linux內驅動的布局。對于一般的驅動,比較復雜的,很難看懂,這個就很不適合進行入門。太簡單的,什么hello、led,這個容易懂,但這個也只能入門,不能用于深入。我感覺RTC驅動用來深入一點的入門是很好的,其中涉及到了platform和驅動設計中的分離思想。下面還是主要以手冊所介紹為步驟,簡述一下
[單片機]
<font color='red'>linux</font>-2.6.32在mini2440開發板上移植-RTC移植
恩智浦推出新一代安全高能效i.MX 91系列,為廣泛的邊緣應用擴展Linux功能
i.MX 9 系列的新成員簡化了高性價比邊緣設備的開發過程,助力構建需要安全性、高性能表現以及Linux支持的可擴展、高可靠性的平臺 中國上海——2023年6月1日—— 恩智浦半導體(NXP Semiconductors N.V.,)正式發布i.MX 91應用處理器系列。 憑借恩智浦二十多年來在開發多市場應用處理器方面的領先優勢,i.MX 91系列提供了安全、多功能、高能效的優化組合,可滿足下一代基于Linux?的物聯網和工業應用的需求。 產品重要性 新發布的協議改變了物聯網和工業市場新產品類別的方向,如面向未來智能家居的可互操作安全連接標準Matter,或面向電動汽車充電器的ISO 15118-20標準。此類新產品通
[嵌入式]
s3c6410的RTC在linux中的驅動(5)
在上一篇中我們在中分析了RTC驅動的注冊和注銷,重點講了平臺設備驅動的probe函數,最后引出了這篇我們要講解的內容,那就是下面這個結構體中的一些函數。 static const struct rtc_class_ops s3c_rtcops = { .open= s3c_rtc_open, .release = s3c_rtc_release, .ioctl = s3c_rtc_ioctl, .read_time = s3c_rtc_gettime, .set_time = s3c_rtc_settime, .read_alarm = s3c_rtc_getalarm, .set_alarm = s3c_rtc_seta
[單片機]
s3c6410的RTC在<font color='red'>linux</font>中的驅動(5)
arm-Linux 編譯動態庫 簡單實例
本文講述了一個簡單的例子 ,很好的解釋了“編譯動態庫”。 一、準備工作 1.使用oracle VM Virtualbox軟件安裝Ubuntu虛擬機 2.下載好相關的軟件并傳輸到虛擬機下,安裝好交叉編譯器。 二、編譯應用 該例子以 1.c , 2.c, 2.h組成 1. 代碼編輯 下面通過一個例子來介紹如何生成一個動態庫。這里有一個頭文件:2.h,一個.c文件:2.c,我們將這個文件編譯成一個動態庫:libtest2.so。 在Linux下編輯代碼,可以使用vi,也可以使用gedit; 使用degit編輯 2.c, 2.h 如下圖所示 然后我們用交叉編譯器命令 :(用交叉編譯是為了讓生
[單片機]
arm-<font color='red'>Linux</font> 編譯動態庫 簡單實例
小廣播
設計資源 培訓 開發板 精華推薦

最新單片機文章
何立民專欄 單片機及嵌入式寶典

北京航空航天大學教授,20余年來致力于單片機與嵌入式系統推廣工作。

 
EEWorld訂閱號

 
EEWorld服務號

 
汽車開發圈

 
機器人開發圈

電子工程世界版權所有 京ICP證060456號 京ICP備10001474號-1 電信業務審批[2006]字第258號函 京公網安備 11010802033920號 Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
主站蜘蛛池模板: 延寿县| 安国市| 平顶山市| 济南市| 洪洞县| 广西| 临桂县| 万源市| 台北市| 农安县| 罗山县| 资兴市| 博罗县| 寿光市| 高青县| 西乌珠穆沁旗| 建阳市| 东台市| 图们市| 阳朔县| 咸丰县| 莒南县| 广州市| 玉环县| 海兴县| 井陉县| 札达县| 高州市| 大庆市| 临城县| 禄丰县| 鲁山县| 黄骅市| 嘉禾县| 霸州市| 慈利县| 盘山县| 陕西省| 清丰县| 伽师县| 德江县|