結合以前對Linux內核的platform總線 ,以及對字符設備的cdev接口的分析,本文將編寫基於platform總線與cdev接口的LED設備的實例代碼並對其進行分析。html
platform總線分析,詳見Linux platform驅動模型。node
字符設備的cdev接口分析,詳見Linux字符設備驅動(一):cdev接口。linux
硬件接口:函數
CPU:s5pv210;post
LED的GPIO:GPIO_J0_3 ~ GPIO_J0_5;測試
LED的工做方式:低電平亮,高電平滅。spa
本文將設備信息寫成一個模塊的形式,須要時加載該模塊便可。指針
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/ioport.h> #include <linux/platform_device.h> //定義並初始化LED設備的相關資源 static struct resource led_resource = { .start = 0xE0200240, .end = 0xE0200240 + 8 - 1, .flags = IORESOURCE_MEM, }; //定義並初始化LED設備信息 static struct platform_device led_dev = { .name = "led", //設備名稱 .id = -1, //設備數量,-1表示只有一個設備 .num_resources = 1, //資源數量 .resource = &led_resource, //資源指針 .dev = { .release = led_release, }, }; //註冊LED設備 static int __init led_device_init(void) { return platform_device_register(&led_dev); } //註銷LED設備 static void __exit led_device_exit(void) { platform_device_unregister(&led_dev); } module_init(led_device_init); module_exit(led_device_exit); MODULE_AUTHOR("Lin"); MODULE_DESCRIPTION("led device for x210"); MODULE_LICENSE("GPL");
led_driver_init():模塊加載函數
platform_driver_register()將驅動對象模塊註冊到平臺總線
led_probe()探測函數,提取相應的信息
platform_get_resource()獲取設備資源
request_mem_region()、ioremap()虛擬內存映射
readl()、write()初始化硬件設備
cdev_alloc()申請cdev內存
cdev_init()初始化cdev對象,將cdev與fops結構體綁定
alloc_chrdev_region()申請設備號
cdev_add()註冊LED設備對象,將cdev添加至系統字符設備鏈表中
class_create()建立設備類
device_create()建立設備文件code
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/ioport.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <asm/io.h> #include <linux/leds.h> #include <asm/uaccess.h> #include <linux/cdev.h> #include <linux/kdev_t.h> #include <linux/ioctl.h> #define LED_IOC_MAGIC 'l' //ioctl幻數 #define LED_IOC_MAXNR 2 //ioctl最大命令序數 #define LED_ON _IO(LED_IOC_MAGIC, 0) //ioctl自定義命令 #define LED_OFF _IO(LED_IOC_MAGIC, 1) #define DEVNAME "led" //設備名稱 static int led_major = 0; //主設備號 static int led_minor = 0; //次設備號 const int led_count = 1; //次設備數量 //GPIO寄存器變量定義 typedef struct GPJ0REG { volatile unsigned int gpj0con; volatile unsigned int gpj0dat; }gpj0_reg_t; static gpj0_reg_t *pGPIOREG = NULL; static dev_t led_devnum; //設備號 static struct cdev *led_cdev = NULL; //設備對象 static struct class *led_cls = NULL; //設備類 static struct device *led_dev = NULL; //設備 //LED設備的ioctl函數實現 static int led_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int reg_value = 0; //檢測命令的有效性 if (_IOC_TYPE(cmd) != LED_IOC_MAGIC) return -EINVAL; if (_IOC_NR(cmd) > LED_IOC_MAXNR) return -EINVAL; //根據命令,執行相應的硬件操做 switch(cmd) { case LED_ON: reg_value = readl(&pGPIOREG->gpj0dat); reg_value &= ~((1 << 3) | (1 << 4) | (1 << 5)); writel(&pGPIOREG->gpj0dat, reg_value); break; case LED_OFF: reg_value = readl(&pGPIOREG->gpj0dat); reg_value |= (1 << 3) | (1 << 4) | (1 << 5); writel(&pGPIOREG->gpj0dat, reg_value); break; default: return -EINVAL; } return 0; } static int led_open(struct inode *inode, struct file *filp) { return 0; } static int led_release(struct inode *inode, struct file *filp) { return 0; } //LED設備的write函數實現 static ssize_t led_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { char kbuf[20] = {0}; int reg_value = 0; memset(kbuf, 0, sizeof(kbuf)); if (copy_from_user(kbuf, user_buf, count)) { return -EFAULT; } if (kbuf[0] == '0') { reg_value = readl(&(pGPIOREG->gpj0dat)); reg_value |= (1 << 3) | (1 << 4) | (1 << 5); writel(reg_value, &(pGPIOREG->gpj0dat)); } else { reg_value = readl(&(pGPIOREG->gpj0dat)); reg_value &= ~((1 << 3) | (1 << 4) | (1 << 5)); writel(reg_value, &(pGPIOREG->gpj0dat)); } return 1; } //定義並初始化LED設備的操做集 static const struct file_operations led_ops = { .owner = THIS_MODULE, .open = led_open, .write = led_write, .ioctl = led_ioctl, .release = led_release, }; //LED設備的probe函數實現 static int led_probe(struct platform_device *pdev) { struct resource *res_led = NULL; int ret = -1; int reg_value = 0; int i = 0; /****************************申請資源*******************************/ //獲取資源 res_led = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res_led) { return -ENOMEM; } //動態內存映射 if (!request_mem_region(res_led->start, resource_size(res_led), "GPIOJ0")) { return -EBUSY; } pGPIOREG = ioremap(res_led->start, resource_size(res_led)); if (pGPIOREG == NULL) { ret = -ENOENT; goto ERR_STEP; } /****************************初始化資源*******************************/ //設置GPIO爲輸出模式 reg_value = readl(&(pGPIOREG->gpj0con)); reg_value |= (1 << (3*4)) | (1 << (4*4)) | (1 << (5*4)); writel(reg_value, &(pGPIOREG->gpj0con)); /***************************建立接口(cdev)***************************/ //申請cdev內存 led_cdev = cdev_alloc(); if (led_cdev == NULL) { ret = -ENOMEM; goto ERR_STEP1; } //初始化led_cdev(將led_cdev於led_ops關聯) cdev_init(led_cdev, &led_ops); //申請設備號 ret = alloc_chrdev_region(&led_devnum, led_minor, led_count, DEVNAME); if (ret < 0) { goto ERR_STEP1; } //註冊LED設備對象(將cdev添加至系統字符設備鏈表中) ret = cdev_add(led_cdev, led_devnum, led_count); if (ret < 0) { goto ERR_STEP2; } //建立設備類 led_cls = class_create(THIS_MODULE, DEVNAME); if (IS_ERR(led_cls)) { ret = PTR_ERR(led_cls); goto ERR_STEP3; } //在設備類下建立設備文件 led_major = MAJOR(led_devnum); for(i = led_minor; i < (led_count + led_minor); i++) { led_dev = device_create(led_cls, NULL, MKDEV(led_major, i), NULL, "%s%d", DEVNAME, i); if(IS_ERR(led_dev)) { ret = PTR_ERR(led_dev); goto ERR_STEP4; } } return 0; /*******************************倒映式錯誤處理*******************************/ ERR_STEP4: for(--i; i >= led_minor; i--) { device_destroy(led_cls, MKDEV(led_major, i)); } class_destroy(led_cls); ERR_STEP3: cdev_del(led_cdev); ERR_STEP2: unregister_chrdev_region(led_devnum, led_count); ERR_STEP1: iounmap(pGPIOREG); ERR_STEP: release_mem_region(res_led->start, resource_size(res_led)); return ret; } int led_remove(struct platform_device *pdev) { int i = 0; //刪除設備文件 for(i = led_minor; i < (led_count + led_minor); i++) { device_destroy(led_cls, MKDEV(led_major, i)); } //刪除設備類 class_destroy(led_cls); //刪除設備對象 cdev_del(led_cdev); //註銷設備號 unregister_chrdev_region(led_devnum, led_count); //釋放內存 iounmap(pGPIOREG); return 0; } //定義並初始化LED驅動信息 static struct platform_driver led_drv = { .driver = { .name = "led", .owner = THIS_MODULE, }, .probe = led_probe, .remove = led_remove, }; //註冊LED驅動 static int __init led_driver_init(void) { return platform_driver_register(&led_drv); } //註銷LED驅動 static void __exit led_driver_exit(void) { platform_driver_unregister(&led_drv); } module_init(led_driver_init); module_exit(led_driver_exit); MODULE_AUTHOR("Lin"); MODULE_DESCRIPTION("led driver for x210"); MODULE_LICENSE("GPL");
運行應用程序以前,需確保上述兩個模塊(device、driver)被裝載。運行結果代表LED設備能被應用程序操做。orm
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #define FILE_NAME "/dev/led0" #define LED_ON _IO(LED_IOC_MAGIC, 0) #define LED_OFF _IO(LED_IOC_MAGIC, 1) char WriteBuf[30]; char ReadBuf[30]; char ScanBuf[30]; int main(void) { int fd = -1; int i = 0; //打開設備文件 if ((fd = open(FILE_NAME, O_RDWR)) < 0) { printf("%s open error\n", FILE_NAME); return -1; } while (1) { memset(ScanBuf, 0, sizeof(ScanBuf)); printf("please input data for LED\n"); if (scanf("%s", ScanBuf)) { //打開LED設備 if (!strcmp(ScanBuf, "on")) { write(fd, "1", 1); } //關閉LED設備 else if (!strcmp(ScanBuf, "off")) { write(fd, "0", 1); } //閃爍LED設備 else if (!strcmp(ScanBuf, "flash")) { for (i=5; i>0; i--) { ioctl(fd, LED_ON); sleep(1); ioctl(fd, LED_OFF); sleep(1); } }
else { break; } } } close(fd); return 0; }