505 lines
14 KiB
Plaintext
505 lines
14 KiB
Plaintext
|
||
//1驱动框架
|
||
1.1静态注册设备
|
||
#include <linux/types.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/ide.h>
|
||
#include <linux/init.h>
|
||
#include <linux/module.h>
|
||
#include <linux/errno.h>
|
||
#include <linux/gpio.h>
|
||
#include <asm/mach/map.h>
|
||
#include <asm/uaccess.h>
|
||
#include <asm/io.h>
|
||
|
||
#define LED_MAJOR 200 /* 主设备号 */
|
||
#define LED_NAME "led" /* 设备名字 */
|
||
|
||
static int led_open(struct inode *inode, struct file *filp)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static int led_release(struct inode *inode, struct file *filp)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static struct file_operations led_fops = {
|
||
.owner = THIS_MODULE,
|
||
.open = led_open,
|
||
.read = led_read,
|
||
.write = led_write,
|
||
.release = led_release,
|
||
};
|
||
|
||
|
||
static int __init led_init(void)
|
||
{
|
||
register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
|
||
return 0;
|
||
}
|
||
|
||
static void __exit led_exit(void)
|
||
{
|
||
unregister_chrdev(LED_MAJOR, LED_NAME);
|
||
}
|
||
|
||
module_init(led_init);
|
||
module_exit(led_exit);
|
||
MODULE_LICENSE("GPL");
|
||
MODULE_AUTHOR("sakura");
|
||
|
||
1.2新字符设备,引用了字符设备结构体
|
||
#include <linux/types.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/ide.h>
|
||
#include <linux/init.h>
|
||
#include <linux/module.h>
|
||
#include <linux/errno.h>
|
||
#include <linux/gpio.h>
|
||
#include <linux/cdev.h>
|
||
#include <linux/device.h>
|
||
#include <asm/mach/map.h>
|
||
#include <asm/uaccess.h>
|
||
#include <asm/io.h>
|
||
|
||
/* newchrled设备结构体 */
|
||
struct newchrled_dev{
|
||
dev_t devid; /* 设备号 */
|
||
struct cdev cdev; /* cdev */
|
||
struct class *class; /* 类 */
|
||
struct device *device; /* 设备 */
|
||
int major; /* 主设备号 */
|
||
int minor; /* 次设备号 */
|
||
};
|
||
|
||
struct newchrled_dev newchrled; /* led设备 */
|
||
|
||
static int led_open(struct inode *inode, struct file *filp)
|
||
{
|
||
filp->private_data = &newchrled; /* 设置私有数据 */
|
||
return 0;
|
||
}
|
||
|
||
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static int led_release(struct inode *inode, struct file *filp)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
/* 设备操作函数 */
|
||
static struct file_operations newchrled_fops = {
|
||
.owner = THIS_MODULE,
|
||
.open = led_open,
|
||
.read = led_read,
|
||
.write = led_write,
|
||
.release = led_release,
|
||
};
|
||
|
||
static int __init led_init(void)
|
||
{
|
||
if (newchrled.major) { /* 定义了设备号 */
|
||
newchrled.devid = MKDEV(newchrled.major, 0);
|
||
register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
|
||
} else { /* 没有定义设备号 */
|
||
alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
|
||
newchrled.major = MAJOR(newchrled.devid); /* 获取分配号的主设备号 */
|
||
newchrled.minor = MINOR(newchrled.devid); /* 获取分配号的次设备号 */
|
||
}
|
||
newchrled.cdev.owner = THIS_MODULE;
|
||
cdev_init(&newchrled.cdev, &newchrled_fops);
|
||
cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);
|
||
newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
|
||
newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static void __exit led_exit(void)
|
||
{
|
||
cdev_del(&newchrled.cdev);/* 删除cdev */
|
||
unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT); /* 注销设备号 */
|
||
device_destroy(newchrled.class, newchrled.devid);
|
||
class_destroy(newchrled.class);
|
||
}
|
||
|
||
module_init(led_init);
|
||
module_exit(led_exit);
|
||
MODULE_LICENSE("GPL");
|
||
MODULE_AUTHOR("zuozhongkai");
|
||
|
||
1.3platform驱动
|
||
#include <linux/types.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/ide.h>
|
||
#include <linux/init.h>
|
||
#include <linux/module.h>
|
||
#include <linux/errno.h>
|
||
#include <linux/gpio.h>
|
||
#include <linux/cdev.h>
|
||
#include <linux/device.h>
|
||
#include <linux/of_gpio.h>
|
||
#include <linux/semaphore.h>
|
||
#include <linux/timer.h>
|
||
#include <linux/irq.h>
|
||
#include <linux/wait.h>
|
||
#include <linux/poll.h>
|
||
#include <linux/fs.h>
|
||
#include <linux/fcntl.h>
|
||
#include <linux/platform_device.h>
|
||
#include <asm/mach/map.h>
|
||
#include <asm/uaccess.h>
|
||
#include <asm/io.h>
|
||
|
||
struct leddev_dev{
|
||
dev_t devid; /* 设备号 */
|
||
struct cdev cdev; /* cdev */
|
||
struct class *class; /* 类 */
|
||
struct device *device; /* 设备 */
|
||
int major; /* 主设备号 */
|
||
};
|
||
|
||
struct leddev_dev leddev; /* led设备 */
|
||
|
||
static int led_open(struct inode *inode, struct file *filp)
|
||
{
|
||
filp->private_data = &leddev; /* 设置私有数据 */
|
||
return 0;
|
||
}
|
||
|
||
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static struct file_operations led_fops = {
|
||
.owner = THIS_MODULE,
|
||
.open = led_open,
|
||
.write = led_write,
|
||
};
|
||
|
||
static int led_probe(struct platform_device *dev)
|
||
{
|
||
if (leddev.major) { /* 定义了设备号 */
|
||
leddev.devid = MKDEV(leddev.major, 0);
|
||
register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
|
||
} else { /* 没有定义设备号 */
|
||
alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME); /* 申请设备号 */
|
||
leddev.major = MAJOR(leddev.devid); /* 获取分配号的主设备号 */
|
||
}
|
||
leddev.cdev.owner = THIS_MODULE;
|
||
cdev_init(&leddev.cdev, &led_fops);
|
||
cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);
|
||
leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
|
||
leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);
|
||
return 0;
|
||
}
|
||
|
||
static int led_remove(struct platform_device *dev)
|
||
{
|
||
cdev_del(&leddev.cdev);/* 删除cdev */
|
||
unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */
|
||
device_destroy(leddev.class, leddev.devid);
|
||
class_destroy(leddev.class);
|
||
return 0;
|
||
}
|
||
|
||
static struct platform_driver led_driver = {
|
||
.driver = {
|
||
.name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */
|
||
},
|
||
.probe = led_probe,
|
||
.remove = led_remove,
|
||
};
|
||
|
||
static int __init leddriver_init(void)
|
||
{
|
||
return platform_driver_register(&led_driver);
|
||
}
|
||
|
||
static void __exit leddriver_exit(void)
|
||
{
|
||
platform_driver_unregister(&led_driver);
|
||
}
|
||
|
||
module_init(leddriver_init);
|
||
module_exit(leddriver_exit);
|
||
MODULE_LICENSE("GPL");
|
||
MODULE_AUTHOR("zuozhongkai");
|
||
|
||
1.4dts的platform驱动
|
||
#include <linux/types.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/ide.h>
|
||
#include <linux/init.h>
|
||
#include <linux/module.h>
|
||
#include <linux/errno.h>
|
||
#include <linux/gpio.h>
|
||
#include <linux/cdev.h>
|
||
#include <linux/device.h>
|
||
#include <linux/of_gpio.h>
|
||
#include <linux/semaphore.h>
|
||
#include <linux/timer.h>
|
||
#include <linux/irq.h>
|
||
#include <linux/wait.h>
|
||
#include <linux/poll.h>
|
||
#include <linux/fs.h>
|
||
#include <linux/fcntl.h>
|
||
#include <linux/platform_device.h>
|
||
#include <asm/mach/map.h>
|
||
#include <asm/uaccess.h>
|
||
#include <asm/io.h>
|
||
|
||
struct leddev_dev{
|
||
dev_t devid; /* 设备号 */
|
||
struct cdev cdev; /* cdev */
|
||
struct class *class; /* 类 */
|
||
struct device *device; /* 设备 */
|
||
int major; /* 主设备号 */
|
||
struct device_node *node; /* LED设备节点 */
|
||
int led0; /* LED灯GPIO标号 */
|
||
};
|
||
|
||
struct leddev_dev leddev; /* led设备 */
|
||
|
||
static int led_open(struct inode *inode, struct file *filp)
|
||
{
|
||
filp->private_data = &leddev; /* 设置私有数据 */
|
||
return 0;
|
||
}
|
||
|
||
static struct file_operations led_fops = {
|
||
.owner = THIS_MODULE,
|
||
.open = led_open,
|
||
.write = led_write,
|
||
};
|
||
|
||
static int led_probe(struct platform_device *dev)
|
||
{
|
||
if (leddev.major) {
|
||
leddev.devid = MKDEV(leddev.major, 0);
|
||
register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
|
||
} else {
|
||
alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);
|
||
leddev.major = MAJOR(leddev.devid);
|
||
}
|
||
cdev_init(&leddev.cdev, &led_fops);
|
||
cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);
|
||
leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
|
||
leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);
|
||
leddev.node = of_find_node_by_path("/gpioled");
|
||
of_get_named_gpio(leddev.node, "led-gpio", 0);
|
||
return 0;
|
||
}
|
||
|
||
static int led_remove(struct platform_device *dev)
|
||
{
|
||
cdev_del(&leddev.cdev); /* 删除cdev */
|
||
unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */
|
||
device_destroy(leddev.class, leddev.devid);
|
||
class_destroy(leddev.class);
|
||
return 0;
|
||
}
|
||
|
||
static const struct of_device_id led_of_match[] = {
|
||
{ .compatible = "atkalpha-gpioled" },
|
||
{ /* Sentinel */ }
|
||
};
|
||
|
||
static struct platform_driver led_driver = {
|
||
.driver = {
|
||
.name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */
|
||
.of_match_table = led_of_match, /* 设备树匹配表 */
|
||
},
|
||
.probe = led_probe,
|
||
.remove = led_remove,
|
||
};
|
||
|
||
static int __init leddriver_init(void)
|
||
{
|
||
return platform_driver_register(&led_driver);
|
||
}
|
||
|
||
static void __exit leddriver_exit(void)
|
||
{
|
||
platform_driver_unregister(&led_driver);
|
||
}
|
||
|
||
module_init(leddriver_init);
|
||
module_exit(leddriver_exit);
|
||
MODULE_LICENSE("GPL");
|
||
MODULE_AUTHOR("zuozhongkai");
|
||
|
||
|
||
|
||
|
||
//一些重要的函数
|
||
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)//要配合cat /proc/devices使用(先查看设备号)
|
||
static inline void unregister_chrdev(unsigned int major, const char *name)
|
||
MAJOR(dev)
|
||
MINOR(dev)
|
||
MKDEV(ma,mi)
|
||
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
|
||
void unregister_chrdev_region(dev_t from, unsigned count)
|
||
copy_from_user(void *to, const void __user *from, unsigned long n)
|
||
copy_to_user(void __user *to, const void *from, unsigned long n)
|
||
void writel (unsigned char data , unsigned short addr ) //4byte
|
||
unsigned char readl (unsigned int addr ) //4byte
|
||
void *memcpy(void *destin, void *source, unsigned n)
|
||
ioremap(cookie,size)
|
||
iounmap (volatile void __iomem *addr)
|
||
of_find_node_by_path("/alphaled") //设备树常用函数,一定先找节点
|
||
of_find_property(dtsled.nd, "compatible", NULL)
|
||
ret = of_property_read_string(dtsled.nd, "status", &str);
|
||
of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
|
||
|
||
//shell
|
||
insmod chrdevbase.ko
|
||
modprobe chrdevbase.ko
|
||
mknod /dev/chrdevbase c 200 0 //创建设备节点文件
|
||
rmmod chrdevbase.ko
|
||
|
||
test:
|
||
#include <linux/types.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/ide.h>
|
||
#include <linux/init.h>
|
||
#include <linux/module.h>
|
||
#include <linux/errno.h>
|
||
#include <linux/gpio.h>
|
||
#include <linux/cdev.h>
|
||
#include <linux/device.h>
|
||
#include <asm/mach/map.h>
|
||
#include <asm/uaccess.h>
|
||
#include <asm/io.h>
|
||
|
||
#define NEWCHRLED_CNT 1
|
||
#define NEWCHRLED_NAME "test"
|
||
|
||
/* newchrled设备结构体 */
|
||
struct newchrled_dev{
|
||
dev_t devid; /* 设备号 */
|
||
struct cdev cdev; /* cdev */
|
||
struct class *class; /* 类 */
|
||
struct device *device; /* 设备 */
|
||
int major; /* 主设备号 */
|
||
int minor; /* 次设备号 */
|
||
};
|
||
|
||
struct newchrled_dev newchrled; /* led设备 */
|
||
|
||
static int led_open(struct inode *inode, struct file *filp)
|
||
{
|
||
filp->private_data = &newchrled; /* 设置私有数据 */
|
||
return 0;
|
||
}
|
||
|
||
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static int led_release(struct inode *inode, struct file *filp)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
/* 设备操作函数 */
|
||
|
||
static int led_release(struct inode *inode, struct file *filp)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
/* 设备操作函数 */
|
||
static struct file_operations newchrled_fops = {
|
||
.owner = THIS_MODULE,
|
||
.open = led_open,
|
||
.read = led_read,
|
||
.write = led_write,
|
||
.release = led_release,
|
||
};
|
||
|
||
static int __init led_init(void)
|
||
{
|
||
if (newchrled.major) { /* 定义了设备号 */
|
||
newchrled.devid = MKDEV(newchrled.major, 0);
|
||
register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
|
||
} else { /* 没有定义设备号 */
|
||
alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
|
||
newchrled.major = MAJOR(newchrled.devid); /* 获取分配号的主设备号 */
|
||
newchrled.minor = MINOR(newchrled.devid); /* 获取分配号的次设备号 */
|
||
}
|
||
newchrled.cdev.owner = THIS_MODULE;
|
||
cdev_init(&newchrled.cdev, &newchrled_fops);
|
||
cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);
|
||
newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
|
||
if (IS_ERR(newchrled.class)) {
|
||
return PTR_ERR(newchrled.class);
|
||
}
|
||
newchrled.device = device_create(newchrled.class, NULL,newchrled.devid, NULL, NEWCHRLED_NAME);
|
||
if (IS_ERR(newchrled.device)) {
|
||
return PTR_ERR(newchrled.device);
|
||
}
|
||
printk(KERN_EMERG"init success\n");
|
||
return 0;
|
||
}
|
||
|
||
static void __exit led_exit(void)
|
||
{
|
||
cdev_del(&newchrled.cdev);/* 删除cdev */
|
||
unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT); /* 注销设备号 */
|
||
device_destroy(newchrled.class, newchrled.devid);
|
||
class_destroy(newchrled.class);
|
||
|
||
printk(KERN_EMERG"exit success\n");
|
||
}
|
||
|
||
module_init(led_init);
|
||
module_exit(led_exit);
|
||
MODULE_LICENSE("GPL");
|
||
MODULE_AUTHOR("luozhikun");
|
||
/****************************************************************************/
|
||
|
||
|
||
/*makefile */
|
||
KERNELDIR := /home/sakurar/linux/IMX6ULL/linux/alientek_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga
|
||
CURRENT_PATH := $(shell pwd)
|
||
obj-m := chrdevbase.o
|
||
|
||
build: kernel_modules
|
||
|
||
kernel_modules:
|
||
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
|
||
clean:
|
||
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean |