新的字元裝置驅動框架
阿新 • • 發佈:2021-12-23
1. 程式碼
主要是給自己看的,所以肯定會出現很多錯誤哈哈哈哈哈#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/io.h> #include <linux/cdev.h> #include <linux/device.h> #define NEWCHRDEV_NAME "newChrLed" #define NEWCHRDEV_COUNT 1 #define LED_MAJOR 200 //主裝置號 #define LED_NAME "led" /* 暫存器的實體地址 */ #define CCM_CCGR1_BASE (0X020C406C) #define SW_MUX_GPIO1_IO03_BASE (0X020E0068) #define SW_PAD_GPIO1_IO03_BASE (0X020E02F4) #define GPIO1_DR_BASE (0X0209C000) #define GPIO1_GDIR_BASE (0X0209C004) #if 1 /* 地址對映後的虛擬地址指標 */ //iomem是虛擬地址的型別 static void __iomem *IMX6U_CCM_CCGR1; static void __iomem *SW_MUX_GPIO1_IO03; static void __iomem *SW_PAD_GPIO1_IO03; static void __iomem *GPIO1_DR; static void __iomem *GPIO1_GDIR; #define LEDOFF 0 /* 關燈 */ #define LEDON 1 /* 開燈 */ #endif /* LED裝置結構體 */ struct newchrled_dev{ struct cdev cDev; /* 字元裝置 */ dev_t devId; /* 裝置號 */ struct class *class; /* 類 */ struct device *device; /* 裝置 */ int major; /* 主裝置號 */ int minor; /* 次裝置號 */ }; static struct newchrled_dev newchrled; /* led裝置 */ /* 驅動開閉讀寫 */ /* LED燈開啟或者關閉 */ static void led_switch(u8 sta) { u32 val = 0; if(sta == LEDON){ val = readl(GPIO1_DR); val &= ~(1 << 3); writel(val, GPIO1_DR); } else if(sta == LEDOFF) { val = readl(GPIO1_DR); val |= (1 << 3); writel(val, GPIO1_DR); } } static int newchrled_open(struct inode *inode, struct file *filp) { /* 在這裡新增私有資料 */ filp->private_data = &newchrled; return 0; } static int newchrled_release(struct inode *inode, struct file *filp) { struct newchrled_dev *dev = (struct newchrled_dev*)filp->private_data; return 0; } static ssize_t newchrled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { int retValue; unsigned char dataBuf[1]; /* 1表示1位,開燈關燈 */ retValue = copy_from_user(dataBuf, buf, count); if(retValue < 0){ printk("kernel write failed! \r\n"); return -EFAULT; } /* 判斷開燈還是關燈 */ led_switch(dataBuf[0]); return 0; } static const struct file_operations newchrled_fops = { .owner = THIS_MODULE, .open = newchrled_open, .write = newchrled_write, .release = newchrled_release }; static void led_init(void) { /* 初始化LED燈 */ int val; /* 1.1 地址對映 */ IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4); SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4); SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4); GPIO1_DR = ioremap(GPIO1_DR_BASE, 4); GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4); /* 1.2 初始化 */ val = readl(IMX6U_CCM_CCGR1); val &= ~(3 << 26); /* 先清楚以前的bit26 27 */ val |= 3 << 26; /* 26 27 置1 */ writel(val, IMX6U_CCM_CCGR1); /* 1.3 設定 GPIO1_IO03 的複用功能,將其複用為 * GPIO1_IO03,最後設定 IO 屬性。 */ writel(0x5, SW_MUX_GPIO1_IO03); /* 設定複用 */ writel(0x10B0, SW_PAD_GPIO1_IO03); /* 設定電氣屬性 */ /* 1.4 設定 GPIO1_IO03 為輸出功能 */ val = readl(GPIO1_GDIR); val &= ~(1 << 3); val |= 1 << 3; writel(val, GPIO1_GDIR); /* 1.5 預設開啟 LED */ val = readl(GPIO1_DR); val &= ~(1 << 3); writel(val, GPIO1_DR); } /* 驅動入口 */ static int __init newchrled_init(void) { unsigned int ret = 0; /* 1.初始化LED */ led_init(); printk("led init success \r\n"); /* 2.分配裝置號 */ newchrled.major = 0; /* 手動申請0,表示需要分配 */ if(newchrled.major){ newchrled.devId = MKDEV(newchrled.major, 0); ret = register_chrdev_region(newchrled.devId, NEWCHRDEV_COUNT, NEWCHRDEV_NAME);/* 主裝置號,申請1個,名字 */ } else { ret = alloc_chrdev_region(&newchrled.devId, 0, NEWCHRDEV_COUNT, NEWCHRDEV_NAME);/* 注意第一個引數是取地址,次裝置號是0 */ newchrled.major = MAJOR(newchrled.devId); newchrled.minor = MINOR(newchrled.devId); } if(ret < 0){ printk("chrdev region error \r\n"); goto fail_devId; } printk("major = %d, minor = %d\r\n", newchrled.major, newchrled.minor); /* 3.註冊字元裝置 */ newchrled.cDev.owner = THIS_MODULE; cdev_init(&newchrled.cDev, &newchrled_fops); /* cdev初始化 */ ret = cdev_add(&newchrled.cDev, newchrled.devId, NEWCHRDEV_COUNT); if(ret < 0){ goto fail_cdev; } printk("register chrdev success \r\n"); /* 4.自動建立裝置節點 */ newchrled.class = class_create(THIS_MODULE, NEWCHRDEV_NAME); if(IS_ERR(newchrled.class)){ ret = PTR_ERR(newchrled.class); goto fail_class; } printk("create class success \r\n"); /* 5. 建立之後要建立裝置*/ /* 下面函式 * 第一個引數代表本類,第二個引數代表父類 * 第三個引數是dev_t * 第四個引數是裝置可能會使用一些資料,一般null * 第五個引數是fint,=xxx 會建立/dev/xxx * */ newchrled.device = device_create(newchrled.class, NULL, newchrled.devId, NULL, NEWCHRDEV_NAME); if(IS_ERR(newchrled.device)){ ret = PTR_ERR(newchrled.device); goto fail_device; } printk("create device success \r\n"); printk("module init... \r\n"); return 0; fail_device: class_destroy(newchrled.class); /* 這塊出問題了,表示前面都沒出問題, 所以把前面釋放t */ fail_class: cdev_del(&newchrled.cDev); fail_cdev: unregister_chrdev_region(newchrled.devId, NEWCHRDEV_COUNT); fail_devId: return ret; } /* 驅動出口 */ static void __exit newchrled_exit(void) { /* 1.刪除字元裝置 */ cdev_del(&newchrled.cDev); printk("delete chrdev success \r\n"); /* 2.登出裝置號 */ unregister_chrdev_region(newchrled.devId, NEWCHRDEV_COUNT); printk("unregister chrdev success \r\n"); /* 3.先摧毀裝置再摧毀類 */ device_destroy(newchrled.class, newchrled.devId); printk("device destroy success \r\n"); /* 4.摧毀類 */ class_destroy(newchrled.class); printk("class destroy success \r\n"); printk("module exit \r\n"); } /* 註冊和解除安裝驅動 */ module_init(newchrled_init); module_exit(newchrled_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Shao Zheming");