1. platform总线
CPU和外部通信的2种方式:地址总线式连接和专用接口式连接。平台总线对应地址总线式连接设备。相比于usb、pci、i2c等物理总线来说,platform总线是虚拟的、抽象出来的。platform总线工作体系在kernel/drivers/base/platform.c中。
1.1 platform_device结构体
platform_device结构体定义在kernel/include/linux/platform_device.h中
struct platform_device {const char * name;int id;struct device dev;u32 num_resources;struct resource * resource;const struct platform_device_id *id_entry;/* arch specific additions */struct pdev_archdata archdata;
};
1.2 platform_driver结构体
platform_driver结构体也定义在/kernel/include/linux/platform_device.h中
struct platform_driver {int (*probe)(struct platform_device *);int (*remove)(struct platform_device *);void (*shutdown)(struct platform_device *);int (*suspend)(struct platform_device *, pm_message_t state);int (*resume)(struct platform_device *);struct device_driver driver;const struct platform_device_id *id_table;
};
1.3 platform总线工作原理
linux系统启动时会在bus系统中注册platform,platform的match函数遍历匹配驱动中的platform_device和platform_driver,将匹配到的驱动调用platform_driver中的probe函数完成驱动的初始化和安装。
static int of_platform_device_probe(struct device *dev)
{int error = -ENODEV;struct of_platform_driver *drv;struct of_device *of_dev;const struct of_device_id *match;drv = to_of_platform_driver(dev->driver);of_dev = to_of_device(dev);if (!drv->probe)return error;of_dev_get(of_dev);match = of_match_device(drv->driver.of_match_table, dev);if (match)error = drv->probe(of_dev, match);if (error)of_dev_put(of_dev);return error;
}
2. 使用platform总线编写LED驱动
2.1 在内核源码中添加led对应的platform_device和platform_driver实例
(1)定义s5pv210_led_platfata结构体,新建kernel/arch/arm/mach-s5pv210/include/mach/leds-gpio.h文件,内容如下:
/* arch/arm/mach-s5pv210/include/mach/leds-gpio.h** Copyright (c) 2006 Simtec Electronics* http://armlinux.simtec.co.uk/* Ben Dooks <ben@simtec.co.uk>** S5PV210 - LEDs GPIO connector** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License version 2 as* published by the Free Software Foundation.
*/#ifndef __ASM_ARCH_LEDSGPIO_H
#define __ASM_ARCH_LEDSGPIO_H "leds-gpio.h"#define S5PV210_LEDF_ACTLOW (1<<0) /* LED is on when GPIO low */
#define S5PV210_LEDF_TRISTATE (1<<1) /* tristate to turn off */struct s5pv210_led_platdata {unsigned int gpio;unsigned int flags;char *name;char *def_trigger;
};#endif /* __ASM_ARCH_LEDSGPIO_H */
(2)s5pv210开发板在kernel/arch/arm/mach-s5pv210/mach-x210.c中:
// x210添加led的platform_device
static struct s5pv210_led_platdata s5pv210_led1_pdata =
{.name = "led1",.gpio = S5PV210_GPJ0(3),.flags = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,.def_trigger = "",
};static struct s5pv210_led_platdata s5pv210_led2_pdata =
{.name = "led2",.gpio = S5PV210_GPJ0(4),.flags = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,.def_trigger = "",
};static struct s5pv210_led_platdata s5pv210_led3_pdata =
{.name = "led3",.gpio = S5PV210_GPJ0(5),.flags = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,.def_trigger = "",
};static struct s5pv210_led_platdata s5pv210_led4_pdata =
{.name = "led4",.gpio = S5PV210_GPD0(1),.flags = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,.def_trigger = "",
};static struct platform_device s5pv210_led1_device =
{.name = "s5pv210_led",.id = 1,.dev = {.platform_data = &s5pv210_led1_pdata,},
};static struct platform_device s5pv210_led2_device =
{.name = "s5pv210_led",.id = 2,.dev = {.platform_data = &s5pv210_led2_pdata,},
};static struct platform_device s5pv210_led3_device =
{.name = "s5pv210_led",.id = 3,.dev = {.platform_data = &s5pv210_led3_pdata,},
};static struct platform_device s5pv210_led4_device =
{.name = "s5pv210_led",.id = 4,.dev = {.platform_data = &s5pv210_led4_pdata,},
};
(3)在platform_device数组中添加上自己编写的device
static struct platform_device *smdkc110_devices[] __initdata = {
#ifdef CONFIG_FIQ_DEBUGGER&s5pv210_device_fiqdbg_uart2,
#endif
#ifdef CONFIG_MTD_ONENAND&s5pc110_device_onenand,
#endif
#ifdef CONFIG_MTD_NAND&s3c_device_nand,
#endif&s5p_device_rtc,
#ifdef CONFIG_SND_S3C64XX_SOC_I2S_V4&s5pv210_device_iis0,&s5pv210_device_iis1,
#endif
#ifdef CONFIG_SND_S3C_SOC_AC97&s5pv210_device_ac97,
#endif
#ifdef CONFIG_SND_S3C_SOC_PCM&s5pv210_device_pcm0,
#endif
#ifdef CONFIG_SND_SOC_SPDIF&s5pv210_device_spdif,
#endif&s3c_device_wdt,#ifdef CONFIG_FB_S3C&s3c_device_fb,
#endif
#ifdef CONFIG_DM9000&s5p_device_dm9000,
#endif#ifdef CONFIG_VIDEO_MFC50&s3c_device_mfc,
#endif
#ifdef CONFIG_TOUCHSCREEN_S3C&s3c_device_ts,
#endif&s3c_device_keypad,
#ifdef CONFIG_S5P_ADC&s3c_device_adc,
#endif
#ifdef CONFIG_VIDEO_FIMC&s3c_device_fimc0,&s3c_device_fimc1,&s3c_device_fimc2,
#endif
#ifdef CONFIG_VIDEO_FIMC_MIPI&s3c_device_csis,
#endif
#ifdef CONFIG_VIDEO_JPEG_V2&s3c_device_jpeg,
#endif
#ifdef CONFIG_VIDEO_G2D&s3c_device_g2d,
#endif
#ifdef CONFIG_VIDEO_TV20&s5p_device_tvout,&s5p_device_cec,&s5p_device_hpd,
#endif&s3c_device_g3d,&s3c_device_lcd,&s3c_device_i2c0,
#ifdef CONFIG_S3C_DEV_I2C1&s3c_device_i2c1,
#endif
#ifdef CONFIG_S3C_DEV_I2C2&s3c_device_i2c2,
#endif#ifdef CONFIG_USB_EHCI_HCD&s3c_device_usb_ehci,
#endif
#ifdef CONFIG_USB_OHCI_HCD&s3c_device_usb_ohci,
#endif#ifdef CONFIG_USB_GADGET&s3c_device_usbgadget,
#endif
#ifdef CONFIG_USB_ANDROID&s3c_device_android_usb,
#ifdef CONFIG_USB_ANDROID_MASS_STORAGE&s3c_device_usb_mass_storage,
#endif
#ifdef CONFIG_USB_ANDROID_RNDIS&s3c_device_rndis,
#endif
#endif
#ifdef CONFIG_BATTERY_S3C&sec_device_battery,
#endif
#ifdef CONFIG_S3C_DEV_HSMMC&s3c_device_hsmmc0,
#endif
#ifdef CONFIG_S3C_DEV_HSMMC1&s3c_device_hsmmc1,
#endif
#ifdef CONFIG_S3C_DEV_HSMMC2&s3c_device_hsmmc2,
#endif
#ifdef CONFIG_S3C_DEV_HSMMC3&s3c_device_hsmmc3,
#endif#ifdef CONFIG_S3C64XX_DEV_SPI&s5pv210_device_spi0,&s5pv210_device_spi1,
#endif
#ifdef CONFIG_S5PV210_POWER_DOMAIN&s5pv210_pd_audio,&s5pv210_pd_cam,&s5pv210_pd_tv,&s5pv210_pd_lcd,&s5pv210_pd_g3d,&s5pv210_pd_mfc,
#endif#ifdef CONFIG_HAVE_PWM&s3c_device_timer[0],&s3c_device_timer[1],&s3c_device_timer[2],&s3c_device_timer[3],
#endif// &timed_gpio_device,&headset_switch_device,&s5pv210_led1_device,&s5pv210_led2_device,&s5pv210_led3_device,&s5pv210_led4_device,
};
2.2 编写led驱动
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/leds.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <mach/gpio.h>
#include <linux/platform_device.h>
#include <mach/leds-gpio.h>
#include <linux/slab.h>struct s5pv210_gpio_led
{struct led_classdev cdev;struct s5pv210_led_platdata* pdata;
};static inline struct s5pv210_gpio_led* pdev_to_gpio(struct platform_device* dev)
{return platform_get_drvdata(dev);
}static inline struct s5pv210_gpio_led* to_gpio(struct led_classdev* led_cdev)
{return container_of(led_cdev, struct s5pv210_gpio_led, cdev);
}static void s5pv210_led_set(struct led_classdev* led_cdev, enum led_brightness value)
{struct s5pv210_gpio_led* s5pv210_led = to_gpio(led_cdev);if (value == LED_OFF){gpio_set_value(s5pv210_led->pdata->gpio, 1);}else{gpio_set_value(s5pv210_led->pdata->gpio, 0);}
}static int s5pv210_led_probe(struct platform_device* dev)
{int ret = 0;struct s5pv210_led_platdata* pdata = dev->dev.platform_data;struct s5pv210_gpio_led* s5pv210_led;s5pv210_led = kzalloc(sizeof(struct s5pv210_gpio_led), GFP_KERNEL);if (s5pv210_led == NULL) {dev_err(&dev->dev, "No memory for device\n");return -ENOMEM;}platform_set_drvdata(dev, s5pv210_led);if (gpio_request(pdata->gpio, pdata->name)){printk(KERN_ERR "gpio_request failed, pdata->gpio = %d, pdata->name = %s\n", pdata->gpio, pdata->name);}else{gpio_direction_output(pdata->gpio, 1);}s5pv210_led->cdev.name = pdata->name;s5pv210_led->cdev.brightness = 0;s5pv210_led->cdev.brightness_set = s5pv210_led_set;s5pv210_led->pdata = pdata;ret = led_classdev_register(&dev->dev, &s5pv210_led->cdev);if (ret < 0) {dev_err(&dev->dev, "led_classdev_register failed\n");kfree(s5pv210_led);return ret;}return 0;
}static int s5pv210_led_remove(struct platform_device* dev)
{struct s5pv210_gpio_led* s5pv210_led = pdev_to_gpio(dev);led_classdev_unregister(&s5pv210_led->cdev);gpio_free(p->pdata->gpio);kfree(s5pv210_led);return 0;
}static struct platform_driver s5pv210_led_driver =
{.probe = s5pv210_led_probe,.remove = s5pv210_led_remove,.driver ={.name = "s5pv210_led",.owner = THIS_MODULE,}
};static int __init led_driver_framework_init(void)
{return platform_driver_register(&s5pv210_led_driver);
}static void __exit led_driver_framework_exit(void)
{platform_driver_unregister(&s5pv210_led_driver);
}module_init(led_driver_framework_init);
module_exit(led_driver_framework_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("xy_L");
MODULE_DESCRIPTION("led_driver_framework");
MODULE_ALIAS("led_driver_framework_test");