linux驱动(四):platform

发布时间:2024年01月10日

? ? ? ? 本文主要探讨x210驱动的平台设备类型(platform)以及misc设备。

驱动模型
????????
设备驱动模型:总线(bus type)、设备(device)和驱动(driver)
????????总线:虚拟总线用于挂接驱动驱动和设备
????????总线、设备、驱动关系:/sys/bus下的子目录对应不同总线,总线目录有设备、驱动目录和、总线属性文件

总线

struct bus_type {
?? ?const char?? ??? ?*name;
?? ?struct bus_attribute?? ?*bus_attrs;
?? ?struct device_attribute?? ?*dev_attrs;
?? ?struct driver_attribute?? ?*drv_attrs;

?? ?int (*match)(struct device *dev, struct device_driver *drv);
?? ?int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
?? ?int (*probe)(struct device *dev);
?? ?int (*remove)(struct device *dev);
?? ?void (*shutdown)(struct device *dev);

?? ?int (*suspend)(struct device *dev, pm_message_t state);
?? ?int (*resume)(struct device *dev);

?? ?const struct dev_pm_ops *pm;

?? ?struct bus_type_private *p;
};

? ? ? ? 总线属性?

struct bus_attribute {
?? ?struct attribute?? ?attr;
?? ?ssize_t (*show)(struct bus_type *bus, char *buf);
?? ?ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};

? ? ? ? 设备属性?

struct device_attribute {
?? ?struct attribute?? ?attr;
?? ?ssize_t (*show)(struct device *dev, struct device_attribute *attr,
?? ??? ??? ?char *buf);
?? ?ssize_t (*store)(struct device *dev, struct device_attribute *attr,
?? ??? ??? ? const char *buf, size_t count);
};

? ? ? ? ?驱动属性? ??

struct driver_attribute {
	struct attribute attr;
	ssize_t (*show)(struct device_driver *driver, char *buf);
	ssize_t (*store)(struct device_driver *driver, const char *buf,
			 size_t count);
};

? ? ? ? ?公共属性结构体(show(read)和store(write)方法)

struct attribute {
?? ?const char?? ??? ?*name;
?? ?struct module?? ??? ?*owner;
?? ?mode_t?? ??? ??? ?mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
?? ?struct lock_class_key?? ?*key;
?? ?struct lock_class_key?? ?skey;
#endif
};

? ? ? ? 总线示例

struct bus_type i2c_bus_type = {
? ? ? ? .name ? ? ? ? ? = "i2c",
? ? ? ? .match ? ? ? ? ?= i2c_device_match,
? ? ? ? .probe ? ? ? ? ?= i2c_device_probe,
? ? ? ? .remove ? ? ? ? = i2c_device_remove,
? ? ? ? .shutdown ? ? ? = i2c_device_shutdown,
? ? ? ? .pm ? ? ? ? ? ? = &i2c_device_pm_ops,
};

????????name总线名称,struct bus_attribute,struct device_attribute,struct driver_attribut为总线,设备,驱动属性和方法
????????match为设备和驱动匹配函数,uevent内核向用户通知事件,probe探测函数,remove移除函数,shutdown关闭函数,suspend挂起,resume唤醒,struct dev_pm_ops *pm;电源管理,struct bus_type_private *p;私有数据

struct bus_type_private {
?? ?struct kset subsys;
?? ?struct kset *drivers_kset;
?? ?struct kset *devices_kset;
?? ?struct klist klist_devices;
?? ?struct klist klist_drivers;
?? ?struct blocking_notifier_head bus_notifier;
?? ?unsigned int drivers_autoprobe:1;
?? ?struct bus_type *bus;
};

????????struct kset subsys;总线子系统,kobj为子系统顶层目录
????????struct kset *drivers_kset;struct kset *devices_kset;挂载到该总线上的驱动和设备集合
????????struct klist klist_devices;struct klist klist_drivers;设备和驱动链表
????????unsigned int drivers_autoprobe:1;注册时自动探测设备
????????struct bus_type *bus;回指针

设备

struct device
{
?? ?struct device?? ??? ?*parent;
?? ?struct device_private?? ?*p;
?? ?struct kobject kobj;
?? ?const char?? ??? ?*init_name; /* initial name of the device */
?? ?struct device_type?? ?*type;
?? ?struct mutex?? ??? ?mutex;?? ?/* mutex to synchronize calls to
?? ??? ??? ??? ??? ? * its driver.
?? ??? ??? ??? ??? ? */

?? ?struct bus_type?? ?*bus;?? ??? ?/* type of bus device is on */
?? ?struct device_driver *driver;?? ?/* which driver has allocated this
?? ??? ??? ??? ??? ? ? device */
?? ?void?? ??? ?*platform_data;?? ?/* Platform specific data, device
?? ??? ??? ??? ??? ? ? core doesn't touch it */
?? ?struct dev_pm_info?? ?power;

#ifdef CONFIG_NUMA
?? ?int?? ??? ?numa_node;?? ?/* NUMA node this device is close to */
#endif
?? ?u64?? ??? ?*dma_mask;?? ?/* dma mask (if dma'able device) */
?? ?u64?? ??? ?coherent_dma_mask;/* Like dma_mask, but for
?? ??? ??? ??? ??? ? ? ? alloc_coherent mappings as
?? ??? ??? ??? ??? ? ? ? not all hardware supports
?? ??? ??? ??? ??? ? ? ? 64 bit addresses for consistent
?? ??? ??? ??? ??? ? ? ? allocations such descriptors. */

?? ?struct device_dma_parameters *dma_parms;

?? ?struct list_head?? ?dma_pools;?? ?/* dma pools (if dma'ble) */

?? ?struct dma_coherent_mem?? ?*dma_mem; /* internal for coherent mem
?? ??? ??? ??? ??? ? ? ? override */
?? ?/* arch specific additions */
?? ?struct dev_archdata?? ?archdata;
#ifdef CONFIG_OF
?? ?struct device_node?? ?*of_node;
#endif

?? ?dev_t?? ??? ??? ?devt;?? ?/* dev_t, creates the sysfs "dev" */

?? ?spinlock_t?? ??? ?devres_lock;
?? ?struct list_head?? ?devres_head;

?? ?struct klist_node?? ?knode_class;
?? ?struct class?? ??? ?*class;
?? ?const struct attribute_group **groups;?? ?/* optional groups */

?? ?void?? ?(*release)(struct device *dev);
}

????????device结构体用于描述设备
????????struct device?? ??? ?*parent;父设备
????????struct device_private?? ?*p;私有数据

struct device_private {
?? ?struct klist klist_children;
?? ?struct klist_node knode_parent;
?? ?struct klist_node knode_driver;
?? ?struct klist_node knode_bus;
?? ?void *driver_data;
?? ?struct device *device;
};

????????const char?? ??? ?*init_name;设备名
????????struct bus_type?? ?*bus;指向总线
????????struct device_type?? ?*type;设备处理函数

??struct device_type {
?? ?const char *name;
?? ?const struct attribute_group **groups;
?? ?int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
?? ?char *(*devnode)(struct device *dev, mode_t *mode);
?? ?void (*release)(struct device *dev);

?? ?const struct dev_pm_ops *pm;
};

????????struct device_driver *driver;设备驱动
????????struct kobject kobj;内嵌kobject

struct kobject {
?? ?const char?? ??? ?*name;
?? ?struct list_head?? ?entry;
?? ?struct kobject?? ??? ?*parent;
?? ?struct kset?? ??? ?*kset;
?? ?struct kobj_type?? ?*ktype;
?? ?struct sysfs_dirent?? ?*sd;
?? ?struct kref?? ??? ?kref;
?? ?unsigned int state_initialized:1;
?? ?unsigned int state_in_sysfs:1;
?? ?unsigned int state_add_uevent_sent:1;
?? ?unsigned int state_remove_uevent_sent:1;
?? ?unsigned int uevent_suppress:1;
};

?????????void?? ?(*release)(struct device *dev);释放设备回调函数
????????dev_t?? ??? ??? ?devt;设备号
????????struct class?? ??? ?*class;设备类类指针
????????void?? ??? ?*platform_data;平台设备数据(gpio、中断号、设备名称等)

设备注册和注销

int device_register(struct device *dev);
void device_unregister(struct device *dev);

宏DEVICE_ATTR定义attribute结构

#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

创建和删除device目录下的属性文件

int device_create_file(struct device *dev,const struct device_attribute *attr);
void device_remove_file(struct device *dev,const struct device_attribute *attr);

驱动

struct device_driver {
?? ?const char?? ??? ?*name;
?? ?struct bus_type?? ??? ?*bus;

?? ?struct module?? ??? ?*owner;
?? ?const char?? ??? ?*mod_name;?? ?/* used for built-in modules */

?? ?bool suppress_bind_attrs;?? ?/* disables bind/unbind via sysfs */

#if defined(CONFIG_OF)
?? ?const struct of_device_id?? ?*of_match_table;
#endif

?? ?int (*probe) (struct device *dev);
?? ?int (*remove) (struct device *dev);
?? ?void (*shutdown) (struct device *dev);
?? ?int (*suspend) (struct device *dev, pm_message_t state);
?? ?int (*resume) (struct device *dev);
?? ?const struct attribute_group **groups;

?? ?const struct dev_pm_ops *pm;

?? ?struct driver_private *p;
}

????????const char?? ??? ?*name;驱动名字与设备名字相同总线match匹配函数才可对应找到
????????struct bus_type?? ??? ?*bus;指向驱动总线
????????const char?? ??? ?*mod_name;驱动模块名字
????????probe驱动探测方法(装载使用),remove删除驱动,shutdown关闭驱动方法,suspend挂起方法,resume唤醒方法
????????struct driver_private *p;设备驱动私有数据

struct driver_private {
?? ?struct kobject kobj;
?? ?struct klist klist_devices;
?? ?struct klist_node knode_bus;
?? ?struct module_kobject *mkobj;
?? ?struct device_driver *driver;
};

????????const struct attribute_group **groups;驱动属性组

struct attribute_group {
?? ?const char?? ??? ?*name;
?? ?mode_t?? ??? ??? ?(*is_visible)(struct kobject *,
?? ??? ??? ??? ??? ? ? ? ?struct attribute *, int);
?? ?struct attribute?? ?**attrs;
};

驱动程序注册和注销

int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);

驱动属性文件删除和创建

int driver_create_file(struct device_driver *drv,?const struct driver_attribute *attr);
void driver_remove_file(struct device_driver *drv,const struct driver_attribute *attr);

platform(drivers/base/platform.c)
????????
平台设备:wdt、i2c、i2s、rtc,adc等
????????CPU与外部通信:地址总线、专用接口
????????平台总线为地址总线式连接设备(soc内部外设)

平台设备(struct platform_device)

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;
};

????????name设备名,struct device?? ?dev;设备结构体
????????const struct platform_device_id?? ?*id_entry;设备ID表
????????struct pdev_archdata?? ?archdata;预留
????????struct resource?? ?* resource;资源数组
????????u32 num_resources;设备使用资源数量

struct resource {
?? ?resource_size_t start;
?? ?resource_size_t end;
?? ?const char *name;
?? ?unsigned long flags;
?? ?struct resource *parent, *sibling, *child;
};
static struct resource s3c_wdt_resource[] = {
? ? ? ? [0] = {
? ? ? ? ? ? ? ? .start ?= S3C_PA_WDT,
? ? ? ? ? ? ? ? .end ? ?= S3C_PA_WDT + SZ_1M - 1,
? ? ? ? ? ? ? ? .flags ?= IORESOURCE_MEM,
? ? ? ? },
? ? ? ? [1] = {
? ? ? ? ? ? ? ? .start ?= IRQ_WDT,
? ? ? ? ? ? ? ? .end ? ?= IRQ_WDT,
? ? ? ? ? ? ? ? .flags ?= IORESOURCE_IRQ,
? ? ? ? }
};

????????start资源起始地址(WTCON),资源结束地址,name资源名,flags资源类型(IORESOURCE_MEM内存资源)
????????start资源起始地址(IRQ_WDT中断开始信号),资源结束地址(IRQ_WDT中断结束信号),flags资源类型(IORESOURCE_IRQ中断资源)
????????flags标志:I/O端口(IORESOURCE_IO)、内存资源(IORESOURCE_MEM)、中断号(IORESOURCE_IRQ)、DMA资源(IORESOURCE_DMA)?
????????struct resource *parent, *sibling, *child;构建资源设备树

平台设备函数

int platform_device_add(struct platform_device *pdev);
void platform_device_del(struct platform_device *pdev);
int platform_device_register(struct platform_device *pdev);
void platform_device_unregister(struct platform_device *pdev);
struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num);

平台设备驱动

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;
};

????????struct device_driver driver;设备驱动结构体
????????const struct platform_device_id *id_table;设备ID表
????????probe驱动探测方法(装载使用),remove删除驱动,shutdown关闭驱动方法,suspend挂起方法,resume唤醒方法

平台驱动注册(probe)和注销(remove)

int platform_driver_register(struct platform_driver *drv);
void platform_driver_unregister(struct platform_driver *drv);

misc
???????
?杂项主设备号通常为10
????????设备类文件:/sys/class/misc

混杂设备

struct miscdevice ?{
?? ?int minor;
?? ?const char *name;
?? ?const struct file_operations *fops;
?? ?struct list_head list;
?? ?struct device *parent;
?? ?struct device *this_device;
?? ?const char *nodename;
?? ?mode_t mode;
};

????????minor次设备号,name设备名,fops设备操作函数,this_device当前设备指针

混杂设备的注册和注销

int misc_register(struct miscdevice * misc);
int misc_deregister(struct miscdevice *misc);

210蜂鸣器(x210-buzzer.c)

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <plat/regs-timer.h>
#include <mach/regs-irq.h>
#include <asm/mach/time.h>
#include <linux/clk.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <plat/gpio-cfg.h>

//定义设备名
#define DEVICE_NAME ? ? "buzzer"

//定义cmd
#define PWM_IOCTL_SET_FREQ?? ??? ?1
#define PWM_IOCTL_STOP?? ??? ??? ?0

//定义信号量
static struct semaphore lock;

//初始化buzzer,设置频率
// TCFG0在Uboot中设置,这里不再重复设置
// Timer0输入频率Finput=pclk/(prescaler1+1)/MUX1=66M/16/16
// TCFG0 = tcnt = (pclk/16/16)/freq;
// PWM0输出频率Foutput =Finput/TCFG0= freq
static void PWM_Set_Freq( unsigned long freq )
{
?? ?unsigned long tcon;
?? ?unsigned long tcnt;
?? ?unsigned long tcfg1;

?? ?struct clk *clk_p;
?? ?unsigned long pclk;

?? ?//unsigned tmp;
?? ?
?? ?//设置GPD0_2为PWM输出
?? ?s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(2));

?? ?tcon = __raw_readl(S3C2410_TCON);
?? ?tcfg1 = __raw_readl(S3C2410_TCFG1);

?? ?//mux = 1/16
?? ?tcfg1 &= ~(0xf<<8);
?? ?tcfg1 |= (0x4<<8);
?? ?__raw_writel(tcfg1, S3C2410_TCFG1);
?? ?
?? ?clk_p = clk_get(NULL, "pclk");
?? ?pclk ?= clk_get_rate(clk_p);

?? ?tcnt ?= (pclk/16/16)/freq;
?? ?
?? ?__raw_writel(tcnt, S3C2410_TCNTB(2));
?? ?__raw_writel(tcnt/2, S3C2410_TCMPB(2));//占空比为50%

?? ?tcon &= ~(0xf<<12);
?? ?tcon |= (0xb<<12);?? ??? ?//disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0
?? ?__raw_writel(tcon, S3C2410_TCON);
?? ?
?? ?tcon &= ~(2<<12);?? ??? ??? ?//clear manual update bit
?? ?__raw_writel(tcon, S3C2410_TCON);
}

//停止buzzer(改为输入模式)
void PWM_Stop( void )
{
?? ?//将GPD0_2设置为input
?? ?s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(0));?? ?
}

//对应open,开锁
static int x210_pwm_open(struct inode *inode, struct file *file)
{
?? ?if (!down_trylock(&lock))
?? ??? ?return 0;
?? ?else
?? ??? ?return -EBUSY;
?? ?
}

//对应close,上锁
static int x210_pwm_close(struct inode *inode, struct file *file)
{
?? ?up(&lock);
?? ?return 0;
}

// PWM:GPF14->PWM0(接收cmd,设置频率,关闭buzzer)
static int x210_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
?? ?switch (cmd)?
?? ?{
?? ??? ?case PWM_IOCTL_SET_FREQ:
?? ??? ??? ?printk("PWM_IOCTL_SET_FREQ:\r\n");
?? ??? ??? ?if (arg == 0)
?? ??? ??? ??? ?return -EINVAL;
?? ??? ??? ?PWM_Set_Freq(arg);
?? ??? ??? ?break;

?? ??? ?case PWM_IOCTL_STOP:
?? ??? ?default:
?? ??? ??? ?printk("PWM_IOCTL_STOP:\r\n");
?? ??? ??? ?PWM_Stop();
?? ??? ??? ?break;
?? ?}

?? ?return 0;
}


static struct file_operations dev_fops = {
? ? .owner ? = ? THIS_MODULE,
? ? .open ? ?= ? x210_pwm_open,
? ? .release = ? x210_pwm_close,?
? ? .ioctl ? = ? x210_pwm_ioctl,
};


//定义msic设备
static struct miscdevice misc = {
?? ?.minor = MISC_DYNAMIC_MINOR,
?? ?.name = DEVICE_NAME,
?? ?.fops = &dev_fops,
};

//驱动装载
static int __init dev_init(void)
{
?? ?int ret;

?? ?init_MUTEX(&lock);
?? ?ret = misc_register(&misc);
?? ?
?? ?/* GPD0_2 (PWMTOUT2) */
?? ?ret = gpio_request(S5PV210_GPD0(2), "GPD0");
?? ?if(ret)
?? ??? ?printk("buzzer-x210: request gpio GPD0(2) fail");
?? ??? ?
?? ?s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);
?? ?s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1));
?? ?gpio_set_value(S5PV210_GPD0(2), 0);

?? ?printk ("x210 "DEVICE_NAME" initialized\n");
? ? ?? ?return ret;
}

//驱动卸载
static void __exit dev_exit(void)
{
?? ?misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.9tripod.com");
MODULE_DESCRIPTION("x210 PWM Driver");

buzzer.c
?

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>


#define DEVNAME         "/dev/buzzer"

#define PWM_IOCTL_SET_FREQ              1
#define PWM_IOCTL_STOP                  0


int main(void)
{
        int fd = -1;

        fd = open(DEVNAME, O_RDWR);
        if (fd < 0)
        {
                perror("open");
                return -1;
        }

        ioctl(fd, PWM_IOCTL_SET_FREQ, 10000);
        sleep(3);
        ioctl(fd, PWM_IOCTL_STOP);

        close(fd);
}

demo:

? ? ? ? 使用平台总线方式驱动led(模仿leds-s3c24xx.c)

Makefile

KERN_DIR = /root/kernel
 
obj-m   += x210-led.o
 
all:
        make -C $(KERN_DIR) M=`pwd` modules 
 
cp:
        cp *.ko /root/rootfs/driver
 
.PHONY: clean
clean:
        make -C $(KERN_DIR) M=`pwd` modules clean

?x210-led.c

#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 <linux/slab.h>
#include <mach/leds_x210-gpio.h>
 
#define LED_OFF 1
#define LED_ON  0

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 *p = to_gpio(led_cdev);

        if(value == LED_OFF)
        {
                gpio_set_value(p->pdata->gpio,LED_OFF);
        }
        else
        {
                gpio_set_value(p->pdata->gpio,LED_ON);
        }
}
 
int s5pv210_led_probe(struct platform_device *dev)
{
        int ret = -1;
        struct s5pv210_led_platdata *pdata = dev->dev.platform_data;
        struct s5pv210_gpio_led *led;

        led = kzalloc(sizeof(struct s5pv210_gpio_led), GFP_KERNEL);
        if (led == NULL) {
                dev_err(&dev->dev, "No memory for device\n");
                return -ENOMEM;
        }

        platform_set_drvdata(dev, led);

        ret = gpio_request(pdata->gpio, pdata->name);

        if (ret != 0) 
        {
                printk(KERN_ERR "gpio_request failed %d\n",ret);
                return ret;
        } 
        else 
        {
                gpio_direction_output(pdata->gpio,GPIOF_OUT_INIT_HIGH);
        }

        led->cdev.name = pdata->name;
        led->cdev.brightness = 0;
        led->cdev.brightness_set = s5pv210_led_set;
        led->pdata = pdata;

        ret = led_classdev_register(&dev->dev, &led->cdev);
        if (ret < 0) 
        {
                printk(KERN_ERR "led classdev register failed\n");
                kfree(led);
                return ret;
        }

        return 0;
}

static int s5pv210_led_remove(struct platform_device *dev)
{
        struct s5pv210_gpio_led *p = pdev_to_gpio(dev);

        led_classdev_unregister(&p->cdev);
        gpio_free(p->pdata->gpio);
        kfree(p);
        return 0;
}
 
struct platform_driver s5pv210_led_driver[] ={
        [0] = {
        .probe          = s5pv210_led_probe,
        .remove         = s5pv210_led_remove,
        .driver         = {
                .name           = "led1",
                .owner          = THIS_MODULE,
        }},
        [1] = {
        .probe          = s5pv210_led_probe,
        .remove         = s5pv210_led_remove,
        .driver         = {
                .name           = "led2",
                .owner          = THIS_MODULE,
        }},
        [2] = {
        .probe          = s5pv210_led_probe,
        .remove         = s5pv210_led_remove,
        .driver         = {
                .name           = "led3",
                .owner          = THIS_MODULE,
        }},
};

 
static int __init led_dev_init(void)
{
        platform_driver_register(&s5pv210_led_driver[0]);
        platform_driver_register(&s5pv210_led_driver[1]);
        platform_driver_register(&s5pv210_led_driver[2]);

        return 0;
}
 
static void __exit led_dev_exit(void)
{
        platform_driver_unregister(&s5pv210_led_driver[0]);
        platform_driver_unregister(&s5pv210_led_driver[1]);
        platform_driver_unregister(&s5pv210_led_driver[2]);
}
 
 
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cxb");
MODULE_DESCRIPTION("led gpio  module");
MODULE_ALIAS("led");

操作流程?

vim arch/arm/mach-s5cpv210/include/mach/leds_x210-gpio.h

添加设备特殊数据结构体(gpio、中断号、设备名称等)

#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 */



vim arch/arm/mach-s5pv210/mach-x210.c

添加设备特殊数据结构体

#include <mach/leds_x210-gpio.h>

定义如下平台总线设备到smdkc110_devices[]前

static struct s5pv210_led_platdata x210_led1_pdata = {
	.name		= "led1",
	.gpio		= S5PV210_GPJ0(3),
	.flags		= S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,
	.def_trigger	= "",
};
 
static struct platform_device x210_led1 = {
	.name		= "led1",
	.id		= 0,
	.dev		= {
		.platform_data	= &x210_led1_pdata,
	},
};
 
static struct s5pv210_led_platdata x210_led2_pdata = {
	.name		= "led2",
	.gpio		= S5PV210_GPJ0(4),
	.flags		= S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,
	.def_trigger	= "",
};
 
static struct platform_device x210_led2 = {
	.name		= "led2",
	.id		= 1,
	.dev		= {
		.platform_data	= &x210_led2_pdata,
	},
};


static struct s5pv210_led_platdata x210_led3_pdata = {
	.name		= "led3",
	.gpio		= S5PV210_GPJ0(5),
	.flags		= S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,
	.def_trigger	= "",
};
 
static struct platform_device x210_led3 = {
	.name		= "led3",
	.id		= 2,
	.dev		= {
		.platform_data	= &x210_led3_pdata,
	},
};


添加如下平台总线设备到smdkc110_devices[]

&x210_led1
&x210_led2
&x210_led3



make -j8  && cp arch/arm/boot/zImage ~/tftp -f  

make cp

结果示例:

文章来源:https://blog.csdn.net/tyro_wzp/article/details/135492153
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。