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
|