建议看看大神嵌入式linux系统的路径规划与经验分享(干货满满)_嵌入式推理能进行路径规划吗-优快云博客
先从框架入手学习。
1、模块加载卸载
static int __init led_init(void)
{
return 0;
}
static void __exit led_exit(void)
{
return 0;
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
2、注册注销字符设备
static struct file_operations leds_fops;
static int __init led_init(void)
{
register_chrdev(250, "anyname", &leds_fops);
return 0;
}
static void __exit led_exit(void)
{
unregister_chrdev(250, "anyname");
return 0;
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
insmod后(使用modprobe没成功,先不管能用就行),cat /proc/devices查看有anyname设备名字和设备号250。
3、丰富file_operations leds_fops的操作函数(基本功能:打开关闭读写)
static int leds_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t leds_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t leds_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static int leds_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations leds_fops = {
.owner = THIS_MODULE,
.open = leds_open,
.read = leds_read,
.write = leds_write,
.release = leds_release,
};
static int __init led_init(void)
{
register_chrdev(250, "anyname", &leds_fops);
return 0;
}
static void __exit led_exit(void)
{
unregister_chrdev(250, "anyname");
return 0;
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
4、更改驱动为动态分配设备号
static int leds_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t leds_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t leds_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static int leds_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations leds_fops = {
.owner = THIS_MODULE,
.open = leds_open,
.read = leds_read,
.write = leds_write,
.release = leds_release,
};
dev_t devid;
struct cdev cdev;
static int __init led_init(void)
{
alloc_chrdev_region(&devid, 0, 1, "leds");
cdev.owner = THIS_MODULE;
cdev_init(&cdev, &leds_fops);
cdev_add(&cdev, devid, 1);
return 0;
}
static void __exit led_exit(void)
{
cdev_del(&cdev);
unregister_chrdev_region(devid, 1);
return 0;
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
5、自动生成设备文件(等同于手动mknod)
static int leds_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t leds_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t leds_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static int leds_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations leds_fops = {
.owner = THIS_MODULE,
.open = leds_open,
.read = leds_read,
.write = leds_write,
.release = leds_release,
};
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
static int __init led_init(void)
{
alloc_chrdev_region(&devid, 0, 1, "leds");
cdev.owner = THIS_MODULE;
cdev_init(&cdev, &leds_fops);
cdev_add(&cdev, devid, 1);
class = class_create(THIS_MODULE, "leds");
device = device_create(class, NULL, devid, NULL, "leds");
return 0;
}
static void __exit led_exit(void)
{
cdev_del(&cdev);
unregister_chrdev_region(devid, 1);
device_destroy(class, devid);
class_destroy(class);
return 0;
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
6、初始化设备
注意:(1)不能直接操作寄存器物理地址,要用ioremap虚拟化成虚拟地址;
(2)不能直接操作虚拟地址,要使用内核函数readl、writel等;
(3)对寄存器的操作遵循“读-改-写”。
#define GPF_CONTROL_ADDR 0x56000050
#define GPF_DATA_ADDR 0x56000050
static void __iomem *gpfcon;
static void __iomem *gpfdat;
static int leds_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t leds_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
unsigned char readbuf[100];
copy_to_user(buf, readbuf, cnt);
return 0;
}
static ssize_t leds_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
unsigned char writebuf[100];
copy_from_user(writebuf, buf, cnt);
return 0;
}
static int leds_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations leds_fops = {
.owner = THIS_MODULE,
.open = leds_open,
.read = leds_read,
.write = leds_write,
.release = leds_release,
};
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
static int __init led_init(void)
{
gpfcon = ioremap(GPF_CONTROL_ADDR, 4);
gpfdat = ioremap(GPF_DATA_ADDR, 4);
val = readl(gpfcon);
val &= ~(3<<(4*2) | 3<<(5*2) | 3<<(6*2));
val |= 1<<(4*2)| 1<<(5*2)| 1<<(6*2);
writel(val, gpfcon);
alloc_chrdev_region(&devid, 0, 1, "leds");
cdev.owner = THIS_MODULE;
cdev_init(&cdev, &leds_fops);
cdev_add(&cdev, devid, 1);
class = class_create(THIS_MODULE, "leds");
device = device_create(class, NULL, devid, NULL, "leds");
return 0;
}
static void __exit led_exit(void)
{
iounmap(gpfcon);
iounmap(gpfdat);
cdev_del(&cdev);
unregister_chrdev_region(devid, 1);
device_destroy(class, devid);
class_destroy(class);
return 0;
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
7、Makefile:
KERNELDIR := /home/embedfire/linux/afeng/linux-4.19-rc3
CURRENT_PATH :=$(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules clean
obj-m += leds_drv.o