参考内核中存在的字符设备驱动进行编写,如misc.c
这个经典的字符设备驱动。
寄存器宏定义:
#define CCM_CCGR1 0x20C406C //打开/关闭时钟的寄存器地址 : 使用一个模块前必须开发它的使用,默认是打开的
#define MUX_PAD_GPIO1_IO04 0x20E006C //GPIO复用寄存器地址 : 复用pin为GPIO功能还是其他功能
#define GPIO1_DR 0x209C000 //GPIO输入输出寄存器地址
#define GPIO1_GDIR 0x209C004 //GPIO方向寄存器 :设置GPIO为输入还是输出模式
#define REG_LEN 4 //寄存器地址长度
对于GPIO的配置使用和STM32等单片机使用寄存器开发是一样的。
static struct resource red_led_resource[] = {
[0] = {
.start = CCM_CCGR1,
.end = (CCM_CCGR1 + REG_LEN - 1),
.flags = IORESOURCE_MEM,
},
[1] = {
.start = MUX_PAD_GPIO1_IO04,
.end = (MUX_PAD_GPIO1_IO04 + REG_LEN - 1),
.flags = IORESOURCE_MEM,
},
[2] = {
.start = GPIO1_DR,
.end = (GPIO1_DR + REG_LEN - 1),
.flags = IORESOURCE_MEM,
},
[3] = {
.start = GPIO1_GDIR,
.end = (GPIO1_GDIR + REG_LEN - 1),
.flags = IORESOURCE_MEM,
},
};
对于硬件的操作就是对寄存器的操作,而硬件的寄存器是映射到内存地址上的,所以就是操作内存。所以flags资源类型选择的是IORESOURCE_MEM,start - 寄存器的其实地址,end - 寄存器的结束地址。
static void red_led_platform_release(struct device *dev)
{
}
static struct platform_device red_led_device = {
.name = "red_led",
.id = -1,
.resource = red_led_resource, //注入硬件资源
.num_resources = ARRAY_SIZE(red_led_resource), //硬件资源数组大小
.dev.release = red_led_platform_release,
};
static int __init red_led_platform_device_init(void)
{
printk("red_led_platform_device_init\r\n");
int ret = platform_device_register(&red_led_device);
return ret;
}
module_init(red_led_platform_device_init);
static void __exit red_led_platform_device_exit(void)
{
printk("red_led_platform_device_exit\r\n");
platform_device_unregister(&red_led_device);
}
平台设备的struct file_operations结构函数还是和传统方式一样的实现。
对于平台驱动方式实现的led驱动的变化:
1、定义平台驱动
static struct platform_driver red_led_platform_driver = {
.probe = led_red_driver_probe,
.remove = led_red_driver_remove,
.driver.name = "red_led",
};
2、在probe()函数中进行初始化工作
static int led_red_driver_probe(struct platform_device *dev)
{
struct resource *ccm_ccgr1 = platform_get_resource(dev, IORESOURCE_MEM, 0); //获取设备中定义的文件资源
struct resource *mux_pad_gpio1_io4 = platform_get_resource(dev, IORESOURCE_MEM, 1);
struct resource *gpio1_dr = platform_get_resource(dev, IORESOURCE_MEM, 2);
struct resource *gpio1_gdir = platform_get_resource(dev, IORESOURCE_MEM, 3);
if (ccm_ccgr1 == NULL || mux_pad_gpio1_io4 == NULL || gpio1_dr == NULL || gpio1_gdir == NULL)
return -1;
_CCM_CCGR1 = ioremap(ccm_ccgr1->start, resource_size(ccm_ccgr1)); //将寄存器物理地址映射为虚拟地址
_MUX_PAD_GPIO1_IO04 = ioremap(mux_pad_gpio1_io4->start, resource_size(mux_pad_gpio1_io4));
_GPIO1_DR = ioremap(gpio1_dr->start, resource_size(gpio1_dr));
_GPIO1_GDIR = ioremap(gpio1_gdir->start, resource_size(gpio1_gdir));
int err;
major = register_chrdev(0, "my_led", &led_drv);
led_class = class_create(THIS_MODULE, "my_led_class");
err = PTR_ERR(led_class);
if (IS_ERR(led_class)) {
unregister_chrdev(major, "my_led");
return -1;
}
device_create(led_class, NULL, MKDEV(major, 0), NULL, "my_led");
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
3、在remove()函数中做卸载/解初始化工作
int led_red_driver_remove(struct platform_device *dev)
{
iounmap(_CCM_CCGR1);
iounmap(_MUX_PAD_GPIO1_IO04);
iounmap(_GPIO1_DR);
iounmap(_GPIO1_GDIR);
printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(led_class, MKDEV(major, 0));
class_destroy(led_class);
unregister_chrdev(major, "my_led");
return 0;
}
4、平台设备的注册/卸载
//平台设备入口函数
static int __init red_led_driver_init(void)
{
printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
int ret = platform_driver_register(&red_led_platform_driver);
return ret;
}
//平台设备出口函数
static void __exit red_led_driver_exit(void)
{
printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&red_led_platform_driver);
}
module_init(red_led_driver_init);
module_exit(red_led_driver_exit);
MODULE_AUTHOR("Ares");
MODULE_LICENSE("GPL");
5、加载/卸载驱动
sudo insmod platform_led_device.ko //安装led platform device
sudo insmod platform_led_driver.ko //安装led platform driver
在加载设备和驱动匹配成功后,会在系统目录/sys/bus/platform/devices/
和/sys/bus/platform/driver/*
出现相应led device 和 led driver的目录