驅動代碼:
#include <linux/errno.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/init.h> #include <linux/serio.h> #include <linux/delay.h> #include <linux/clk.h> #include <linux/miscdevice.h> #include <linux/gpio.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/uaccess.h> #include <mach/regs-clock.h> #include <plat/regs-timer.h> #include <mach/regs-gpio.h> #include <linux/cdev.h> //-------------class_create,device_create------ #include <linux/device.h> /*用udev機制自動添加設備節點*/ struct class *led_class; static int led_major = 0; /* 主設備號 */ static struct cdev LedDevs; /* 應用程序執行ioctl(fd, cmd, arg)時的第2個參數 */ #define LED_MAGIC 'k' #define IOCTL_LED_ON _IOW (LED_MAGIC, 1, int) #define IOCTL_LED_OFF _IOW (LED_MAGIC, 2, int) #define IOCTL_LED_RUN _IOW (LED_MAGIC, 3, int) #define IOCTL_LED_SHINE _IOW (LED_MAGIC, 4, int) #define IOCTL_LED_ALLON _IOW (LED_MAGIC, 5, int) #define IOCTL_LED_ALLOFF _IOW (LED_MAGIC, 6, int) /* 用來指定LED所用的GPIO引腳 */ static unsigned long led_table [] = { S5PV210_MP04(4), S5PV210_MP04(5), S5PV210_MP04(6), S5PV210_MP04(7), }; #define LED_NUM ARRAY_SIZE(led_table) /* 應用程序對設備文件/dev/led執行open(...)時, * 就會調用leds_open函數 */ static int leds_open(struct inode *inode, struct file *file) { int i; for (i = 0; i < 4; i++) { // 設置GPIO引腳的功能:本驅動中LED所涉及的GPIO引腳設為輸出功能 s3c_gpio_cfgpin(led_table[i], S3C_GPIO_OUTPUT); } return 0; } //LEDS all light on static void leds_all_on() { int i; for (i=0; i<4; i++) { gpio_set_value(led_table[i], 0); } } //LEDs all light off static void leds_all_off() { int i; for (i=0; i<4; i++) { gpio_set_value(led_table[i], 1); } } /* 應用程序對設備文件/dev/leds執行ioctl(...)時, * 就會調用leds_ioctl函數 */ static int leds_ioctl(struct file *file, unsigned int cmd, unsigned long arg)//沒有inode,用邋unlocked_ioctl { printk("in the leds_ioctl!!\n"); // if (__get_user(data, (unsigned int __user *)arg)) //方法二:指針參數傳遞 // return -EFAULT; printk("arg is %d!!\n",arg); switch(cmd) { case IOCTL_LED_ON: printk("in the IOCTL_LED_ON!!\n"); // 設置指定引腳的輸出電平為0 gpio_set_value(led_table[arg], 0); break; case IOCTL_LED_OFF: printk("in the IOCTL_LED_OFF!!\n"); // 設置指定引腳的輸出電平為1 gpio_set_value(led_table[arg], 1); break; case IOCTL_LED_RUN: // 跑馬燈 { printk("in the IOCTL_LED_RUN!!\n"); int i,j; leds_all_off(); //printk("IOCTL_LED_RUN"); for (i=0;i<arg;i++) for (j=0;j<4;j++) { gpio_set_value(led_table[j], 0); mdelay(400); //delay 400ms gpio_set_value(led_table[j], 1); mdelay(400); //delay 400ms } break; } case IOCTL_LED_SHINE: // LED 閃爍 { printk("in the IOCTL_LED_SHINE!!\n"); int i,j; leds_all_off(); printk("IOCTL_LED_SHINE\n"); for (i=0;i<arg;i++) { for (j=0;j<4;j++) gpio_set_value(led_table[j], 0); mdelay(400); //delay 400ms for (j=0;j<4;j++) gpio_set_value(led_table[j], 1); mdelay(400); } break ; } case IOCTL_LED_ALLON: printk("in the IOCTL_LED_ALLON!!\n"); // 設置指定引腳的輸出電平為0 leds_all_on(); break; case IOCTL_LED_ALLOFF: printk("in the IOCTL_LED_ALLOFF!!\n"); // 設置指定引腳的輸出電平為1 leds_all_off(); break; default: printk("in the default!!\n"); return -EINVAL; } return 0; } /* 這個結構是字符設備驅動程序的核心 * 當應用程序操作設備文件時所調用的open、read、write等函數, * 最終會調用這個結構中指定的對應函數 */ static struct file_operations leds_fops = { .owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */ .open = leds_open, .unlocked_ioctl = leds_ioctl, }; /* * Set up the cdev structure for a device. */ static void led_setup_cdev(struct cdev *dev, int minor, struct file_operations *fops) { int err, devno = MKDEV(led_major, minor); cdev_init(dev, fops); dev->owner = THIS_MODULE; dev->ops = fops; err = cdev_add (dev, devno, 1); /* Fail gracefully if need be */ if (err) printk (KERN_NOTICE "Error %d adding Led%d", err, minor); } /* * 執行“insmod leds.ko”命令時就會調用這個函數 */ static int __init leds_init(void) { int result; dev_t dev = MKDEV(led_major, 0); char dev_name[]="led"; /* 加載模式后,執行”cat /proc/devices”命令看到的設備名稱 */ /*gpio_request*/ int i,ret; for (i = 0; i < LED_NUM; i++) { ret=gpio_request(led_table[i],"LED"); if(ret)//注意,是ret { printk("%s:request GPIO %d for LED failed,ret= %d\n",dev_name,led_table[i],ret); return ret; } s3c_gpio_cfgpin(led_table[i],S3C_GPIO_SFN(1));//output gpio_set_value(led_table[i],1); } /* Figure out our device number. */ if (led_major) result = register_chrdev_region(dev, 1, dev_name); else { result = alloc_chrdev_region(&dev, 0, 1, dev_name); led_major = MAJOR(dev); } if (result < 0) { printk(KERN_WARNING "leds: unable to get major %d\n", led_major); return result; } if (led_major == 0) led_major = result; /* Now set up cdev. */ led_setup_cdev(&LedDevs, 0, &leds_fops); /*udev機制可以自動添加設備節點,只需要添加xxx_class這個類,以及device_create()*/ led_class = class_create(THIS_MODULE, "led_class");/*在sys目錄下創建xx_class這個類,/sys/class/~*/ device_create(led_class, NULL, LedDevs.dev, dev_name, dev_name);/*自動創建設備/dev/$DEVICE_NAME*/ printk("Led device installed, with major %d\n", led_major); printk("The device name is: %s\n", dev_name); return 0; } /* * 執行”rmmod leds”命令時就會調用這個函數 */ static void __exit leds_exit(void) { /*gpio_free*/ int i; for (i = 0; i < LED_NUM; i++) { gpio_free(led_table[i]); } /* 卸載驅動程序 */ cdev_del(&LedDevs); unregister_chrdev_region(MKDEV(led_major, 0), 1); printk("Led device uninstalled\n"); } /* 這兩行指定驅動程序的初始化函數和卸載函數 */ module_init(leds_init); module_exit(leds_exit); /* 描述驅動程序的一些信息,不是必須的 */ MODULE_AUTHOR(""); // 驅動程序的作者 MODULE_DESCRIPTION("LED Driver"); // 一些描述信息 MODULE_LICENSE("Dual BSD/GPL"); // 遵循的協議
測試代碼:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #define LED_MAGIC 'k' #define IOCTL_LED_ON _IOW (LED_MAGIC, 1, int) #define IOCTL_LED_OFF _IOW (LED_MAGIC, 2, int) #define IOCTL_LED_RUN _IOW (LED_MAGIC, 3, int) #define IOCTL_LED_SHINE _IOW (LED_MAGIC, 4, int) #define IOCTL_LED_ALLON _IOW (LED_MAGIC, 5, int) #define IOCTL_LED_ALLOFF _IOW (LED_MAGIC, 6, int) /* led_test on //對應四個LED全亮 led_test off // 對應四個LED全滅 led_test run // 運行跑馬燈實驗 led_test shine //4個LED燈全滅、全亮交替閃爍 led_test 1 on //對應LED1點亮 led_test 1 off // 對應LED1熄滅 ... led_test 4 on //對應LED4點亮 led_test 4 off // 對應LED4熄滅 */ void usage(char *exename) { printf("Usage:\n"); printf(" %s <led_no> <on/off>\n", exename); printf(" led_no = 1, 2, 3 or 4\n"); } int main(int argc, char **argv) { unsigned int led_no; int fd = -1; unsigned int count=10; if (argc > 3 || argc == 1) goto err; fd = open("/dev/led", 0); // 打開設備 if (fd < 0) { printf("Can't open /dev/led\n"); return -1; } if (argc == 2) { if (!strcmp(argv[1], "on")) { ioctl(fd, IOCTL_LED_ALLON, count); // 點亮它 } else if (!strcmp(argv[1], "off")) { ioctl(fd, IOCTL_LED_ALLOFF, count); // 熄滅它 } else if (!strcmp(argv[1], "run")) { ioctl(fd, IOCTL_LED_RUN, count); //運行跑馬燈 } else if (!strcmp(argv[1], "shine")) { ioctl(fd, IOCTL_LED_SHINE, count); //閃爍 } else { goto err; } } if (argc == 3) { led_no = atoi(argv[1]); // 操作哪個LED? if (led_no > 3) goto err; if (!strcmp(argv[2], "on")) { ioctl(fd, IOCTL_LED_ON, led_no); // 點亮 } else if (!strcmp(argv[2], "off")) { ioctl(fd, IOCTL_LED_OFF, led_no); // 熄滅 } else { goto err; } } close(fd); return 0; err: if (fd > 0) close(fd); usage(argv[0]); return -1; }