大多數書籍在介紹字符驅動過於理論化,縱覽一章都是些文字,再附上一些零碎的代碼,看的人頭暈,時間長了自然就不想看了。 對於驅動的學習,剛開始不能過於理論化,一定要結合實際,要不然像空中樓台,住在上面,心裡老感覺不踏實。那麼如何入手呢?我覺得三點是很重要的:
1 驅動設計的總體框架(對於每種類型的驅動設計,最好畫出模型圖)
2 參考現有實例化的驅動
3 針對某一具體硬件,自己寫驅動來實現
接下來以字符驅動設計為例,也是mini2440led驅動實現。
1 字符設備驅動模型如下圖所示,這是一個總體調用框架圖,具體的字符設備驅動模型參照另外一篇引用的文章【字符設備驅動模型】,驅動層主要做的工作是file_operations結構體中一些關鍵函數的實現,包括open,read,ioctl。本例中主要實現open,ioctl。
2 現有驅動模型實例
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/delay.h>
- #include <asm/irq.h>
- #include <asm/arch/regs-gpio.h>
- #include <asm/hardware.h>
-
- #define DEVICE_NAME "leds" /* 加載模式後,執行”cat /proc/devices”命令看到的設備名稱 */
- #define LED_MAJOR 231 /* 主設備號 */
-
- /* 應用程序執行ioctl(fd, cmd, arg)時的第2個參數 */
- #define IOCTL_LED_ON 0
- #define IOCTL_LED_OFF 1
-
- /* 用來指定LED所用的GPIO引腳 */
- static unsigned long led_table [] = {
- S3C2410_GPB5,
- S3C2410_GPB6,
- S3C2410_GPB7,
- S3C2410_GPB8,
- };
-
- /* 用來指定GPIO引腳的功能:輸出 */
- static unsigned int led_cfg_table [] = {
- S3C2410_GPB5_OUTP,
- S3C2410_GPB6_OUTP,
- S3C2410_GPB7_OUTP,
- S3C2410_GPB8_OUTP,
- };
-
- /* 應用程序對設備文件/dev/leds執行open(...)時,
- * 就會調用s3c24xx_leds_open函數
- */
- static int s3c24xx_leds_open(struct inode *inode, struct file *file)
- {
- int i;
-
- for (i = 0; i < 4; i++) {
- // 設置GPIO引腳的功能:本驅動中LED所涉及的GPIO引腳設為輸出功能
- s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
- }
- return 0;
- }
-
- /* 應用程序對設備文件/dev/leds執行ioclt(...)時,
- * 就會調用s3c24xx_leds_ioctl函數
- */
- static int s3c24xx_leds_ioctl(
- struct inode *inode,
- struct file *file,
- unsigned int cmd,
- unsigned long arg)
- {
- if (arg > 4) {
- return -EINVAL;
- }
-
- switch(cmd) {
- case IOCTL_LED_ON:
- // 設置指定引腳的輸出電平為0
- s3c2410_gpio_setpin(led_table[arg], 0);
- return 0;
-
- case IOCTL_LED_OFF:
- // 設置指定引腳的輸出電平為1
- s3c2410_gpio_setpin(led_table[arg], 1);
- return 0;
-
- default:
- return -EINVAL;
- }
- }
-
- /* 這個結構是字符設備驅動程序的核心
- * 當應用程序操作設備文件時所調用的open、read、write等函數,
- * 最終會調用這個結構中指定的對應函數
- */
- static struct file_operations s3c24xx_leds_fops = {
- .owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */
- .open = s3c24xx_leds_open,
- .ioctl = s3c24xx_leds_ioctl,
- };
-
- /*
- * 執行“insmod s3c24xx_leds.ko”命令時就會調用這個函數
- */
- static int __init s3c24xx_leds_init(void)
- {
- int ret;
-
- /* 注冊字符設備驅動程序
- * 參數為主設備號、設備名字、file_operations結構;
- * 這樣,主設備號就和具體的file_operations結構聯系起來了,
- * 操作主設備為LED_MAJOR的設備文件時,就會調用s3c24xx_leds_fops中的相關成員函數
- * LED_MAJOR可以設為0,表示由內核自動分配主設備號
- */
- ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
- if (ret < 0) {
- printk(DEVICE_NAME " can't register major number\n");
- return ret;
- }
-
- printk(DEVICE_NAME " initialized\n");
- return 0;
- }
-
- /*
- * 執行”rmmod s3c24xx_leds.ko”命令時就會調用這個函數
- */
- static void __exit s3c24xx_leds_exit(void)
- {
- /* 卸載驅動程序 */
- unregister_chrdev(LED_MAJOR, DEVICE_NAME);
- }
-
- /* 這兩行指定驅動程序的初始化函數和卸載函數 */
- module_init(s3c24xx_leds_init);
- module_exit(s3c24xx_leds_exit);
-
- /* 描述驅動程序的一些信息,不是必須的 */
- MODULE_AUTHOR("http://my.csdn.net/czxyhll."); // 驅動程序的作者
- MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver"); // 一些描述信息
- MODULE_LICENSE("GPL"); // 遵循的協議