首页 生活指南 正文内容

platformdriverregister(platform driver register)

阿立指南 生活指南 2022-09-28 02:09:08 261 0

platoform怎么玩

platform的灵魂是:device(设备)driver(驱动)platform_bus(platform总线),其特点是设备,驱动分层动态的管理和加载

其中platform_bus是一个虚拟的总线,当我们将设备和驱动注册到虚拟总线上(内核)时,如果该设备是该驱动的设备,该驱动是该设备的驱动,在他们注册时,会互相寻找

一次对方(只在注册的时候寻找一次,找完了就玩了)。这个找的过程是platform_bus来完成的,我们暂不管他如何让寻找。如果device和driver中的name这个字符串是想相同的话

platform_bus就会调用driver中的.probe函数.这个匹配到调用probe的过程是自动的,有总线自己完成。这个过程从注册开始,从probe结束

设备和驱动的关系是多对一的关系,即多个相同设备可使用一个driver,靠device(设备)中的id号来区别

platform的使用其实就四步:

1)初始化 resource 结构变量

2)初始化 platform_device 结构变量

3)向系统注册设备:platform_device_register。

4)想系统注册驱动:[platform_driver_register()]

登录后复制

drvier和device匹配的方法有3种:

* 当一个设备注册的时候,他会在总线上寻找匹配的driver,platform device一般在系统启动很早的时候就注册了

* 当一个驱动注册[platform_driver_register()]的时候,他会遍历所有总线上的设备来寻找匹配,在启动的过程驱动的注册一般比较晚,或者在模块载入的时候

* 当一个驱动注册[platform_driver_probe()]的时候, 功能上和使用platform_driver_register()是一样的,唯一的区别是它不能被以后其他的device probe了,也就是说这个driver只能和 一个device绑定

eg:定义一个driver

platformdriverregister(platform driver register) 第1张

usb hub的linux驱动问题求教,多谢

static int __init ohci_hcd_mod_init(void)

{

platform_driver_register(ohci_hcd_s3c2410_driver);

}

其实真正注册的是ohci_hcd_s3c2410_driver这个驱动。那我们来看一下这个结构体的具体值。

static struct platform_driver ohci_hcd_s3c2410_driver= {

.probe = ohci_hcd_s3c2410_drv_probe,

.remove = ohci_hcd_s3c2410_drv_remove,

.shutdown = usb_hcd_platform_shutdown,

.driver = {

.owner = THIS_MODULE,

.name = "s3c2410-ohci",

},

};

那我们一一来看上述的每一个函数的实现。

2.1 hcd 探测

函数很简单其实现功能的是usb_hcd_s3c2410_probe函数。

static int ohci_hcd_s3c2410_drv_probe(structplatform_device *pdev)

{

returnusb_hcd_s3c2410_probe(ohci_s3c2410_hc_driver, pdev);

}

ohci_s3c2410_hc_driver提供了对于ohci的操作集。对于这些函数在后面的学习中去看,在此不加扩展。我们将下面的函数剔除枝叶留其主干。

static int usb_hcd_s3c2410_probe (const structhc_driver *driver,

struct platform_device *dev)

{

structusb_hcd *hcd = NULL;

int retval;

#if !defined(CONFIG_ARCH_2410)

usb_host_clk_en(); --使能clk

#endif

s3c2410_usb_set_power(dev-dev.platform_data,1, 1);

s3c2410_usb_set_power(dev-dev.platform_data,2, 1);

hcd =usb_create_hcd(driver, dev-dev, "s3c24xx"); --创建一个hcd

hcd-rsrc_start= dev-resource[0].start; --获取物理地址

hcd-rsrc_len = dev-resource[0].end -dev-resource[0].start + 1;

request_mem_region(hcd-rsrc_start,hcd-rsrc_len, hcd_name);

clk =clk_get(dev-dev, "usb-host");

s3c2410_start_hc(dev,hcd);

hcd-regs= ioremap(hcd-rsrc_start, hcd-rsrc_len);

ohci_hcd_init(hcd_to_ohci(hcd));

retval = usb_add_hcd(hcd,dev-resource[1].start, IRQF_DISABLED);

return 0;

}

对于usb的电源管理,我们暂时不看,不看不代表不重要,电源管理是很重要的。

那依次来看上面的函数。usb_create_hcd创建和初始化一个hcd结构体。

s3c2410_start_hc启动hc。这里有一个很奇怪的结构体就是struct s3c2410_hcd_info,在s3c6410中并没有看到该结构体的赋值。也许有人对此很困惑,该结构体做什么用的。那我们来看该结构体的真正面目。

struct s3c2410_hcd_info {

structusb_hcd *hcd; --保存该hcd_info所属的hcd

structs3c2410_hcd_portport[2]; --两个端口。

void (*power_control)(intport, int to); --电源控制

void (*enable_oc)(structs3c2410_hcd_info *, int on);

void (*report_oc)(structs3c2410_hcd_info *, int ports);

};

在usb-host.txt中对其功能进行了说明,就是一对函数,使能过流检测和控制端口电源状态。

power_control:使能或禁止端口电源

enable_oc :使能或禁止端口过流检测

report_oc :当端口存在过流,则会调用该函数。

static void s3c2410_start_hc(structplatform_device *dev, struct usb_hcd *hcd)

{

structs3c2410_hcd_info *info = dev-dev.platform_data;

clk_enable(clk);

if (info !=NULL) { --在s3c6410中该info为空。

info-hcd = hcd;

info-report_oc= s3c2410_hcd_oc;

if(info-enable_oc != NULL) {

(info-enable_oc)(info,1);

}

}

}

初始化ohci_hcd

static void ohci_hcd_init(structohci_hcd *ohci)

{

ohci-next_statechange= jiffies;

spin_lock_init(ohci-lock);

INIT_LIST_HEAD(ohci-pending);

}

初始化并注册usb_hcd

完成通用hcd的初始化和注册,在这里同时完成中断的申请和注册。

int usb_add_hcd(struct usb_hcd *hcd,unsigned intirqnum, unsigned long irqflags)

{

int retval;

structusb_device *rhdev;

hcd-authorized_default= hcd-wireless? 0 : 1; --判断是否为无线

set_bit(HCD_FLAG_HW_ACCESSIBLE,hcd-flags); --设置HW_ACCESSIBLE旗标

if ((retval =hcd_buffer_create(hcd)) != 0) { --开辟hcd的缓冲区

returnretval;

}

if ((retval =usb_register_bus(hcd-self)) 0)

gotoerr_register_bus;

if ((rhdev =usb_alloc_dev(NULL, hcd-self, 0)) == NULL) {

retval= -ENOMEM;

gotoerr_allocate_root_hub;

}

rhdev-speed= (hcd-driver-flags HCD_USB2) ? USB_SPEED_HIGH :USB_SPEED_FULL;--指定根hub的speed

hcd-self.root_hub= rhdev;

device_init_wakeup(rhdev-dev,1);

if(hcd-driver-reset (retval = hcd-driver-reset(hcd)) 0) {--为NULL

gotoerr_hcd_driver_setup;

}

if(device_can_wakeup(hcd-self.controller)

device_can_wakeup(hcd-self.root_hub-dev))

dev_dbg(hcd-self.controller,"supports USB remote wakeup\n");

if(hcd-driver-irq) { --中断处理

if(irqflags IRQF_SHARED)

irqflags= ~IRQF_DISABLED;

snprintf(hcd-irq_descr,sizeof(hcd-irq_descr), "%s:usb%d",

hcd-driver-description,hcd-self.busnum);

request_irq(irqnum,usb_hcd_irq, irqflags,hcd-irq_descr, hcd);--申请中断线

}

hcd-irq= irqnum;

} else {

hcd-irq= -1;

}

hcd-driver-start(hcd); --调用start为 ohci_s3c2410_start

rhdev-bus_mA= min(500u, hcd-power_budget);

register_root_hub(hcd)); --注册root hub

retval =sysfs_create_group(rhdev-dev.kobj, usb_bus_attr_group);

if (retval 0) {

gotoerror_create_attr_group;

}

if(hcd-uses_new_polling hcd-poll_rh)

usb_hcd_poll_rh_status(hcd);

returnretval;

}

那一一来看上面的函数,学习内核就要有打破砂锅问到底的精神,唯有知道那背后的种种风光,才能领略那种种风采。闲话不说,继续!

记住下面结构体中flag的值。那就看这几个宏定义是什么意思。

#defineHCD_MEMORY 0x0001 --hc的寄存器使用memory映射

#defineHCD_LOCAL_MEM 0x0002 --hc使用local memory

#defineHCD_USB11 0x0010 --usb1.1

#defineHCD_USB2 0x0020 --usb2.0

static const struct hc_driver ohci_s3c2410_hc_driver=

{

.flags = HCD_USB11 | HCD_MEMORY,

};

为hcd分配缓冲池,当hc需要使用DMA内存分配器。

int hcd_buffer_create(struct usb_hcd *hcd)

{

char name[16];

int i, size;

if(!hcd-self.controller-dma_mask

!(hcd-driver-flags HCD_LOCAL_MEM))

return 0;

--#define HCD_BUFFER_POOLS 4

我们查看pool_max其实是一个全局数组。如果需要开辟的缓冲区更大的话,直接采用分配page的函数。

static const size_tpool_max[HCD_BUFFER_POOLS] = {

32,128,512,PAGE_SIZE/ 2

};

for (i = 0; i HCD_BUFFER_POOLS; i++) {

size = pool_max[i];

if(!size)

continue;

snprintf(name,sizeof name, "buffer-%d", size);

hcd-pool[i] = dma_pool_create(name,hcd-self.controller,size, size, 0);

if(!hcd-pool [i]) {

hcd_buffer_destroy(hcd);

return-ENOMEM;

}

}

return 0;

}

dma_pool_create创建一个DMA池(生成一个dma_pool,并没有分配相应空间,真正分配物理内存将在dma_pool_alloc()总实现)。

下面的函数是usb_bus注册,对于该函数也许很难理解。不过参照网上的说明,估计会好理解很多。

每个主机控制器拥有一个USB系统,称为一个USB总线。USBD支持多个主机控制器,即多个USB总线。当每增加一个主机控制器时,会给他分配一个usb_bus结构。USBD动态安装和卸载主机驱动。主机驱动安装时,他的初始化函数一方面完成主机控制器硬件的设置和初始化工作,另一方面调用usb_alloc_bus和usb_register_bus来将自己注册到USBD中去,供USB子系统访问。

static int usb_register_bus(struct usb_bus *bus)

{

int result =-E2BIG;

int busnum;

mutex_lock(usb_bus_list_lock);

busnum =find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);

--用busmap来存储主机驱动,一个bit位代表一个主机驱动

if (busnum =USB_MAXBUS) {

return result;

}

set_bit (busnum,busmap.busmap);

bus-busnum = busnum;

bus-dev =device_create(usb_host_class, bus-controller, MKDEV(0, 0),bus,"usb_host%d", busnum);

--在usb_host类下创建一个usb_host设备。

list_add(bus-bus_list, usb_bus_list);

mutex_unlock(usb_bus_list_lock);

usb_notify_add_bus(bus);

return 0;

}

4412开发板怎么控制LED发光二极管

从上面的原理图可以看到两个LED的一端连接到电源VSYS上,另一端通过三极管接地,通过控制三极管的基极,可以点亮或关闭LED。两个三极管的基极分别通过底板连接到核心板上Exynos 4412的GPIO GPL2_0和GPK1_1上。

上一章节已经讲过iTOP-4412开发板中GPIO的驱动,LED的驱动里面将会用到上一章节介绍的几个操作GPIO的函数。

LED驱动的入口函数是leds_init,其实现如下:

static int __init leds_init(void)

{

        return platform_driver_register(leds_driver);

}

该函数会调用内核函数platform_driver_register向内核注册一个硬件设备,这个函数的参数是一个platform_driver结构,leds_driver定义如下:

static struct platform_driver leds_driver = {

        .probe = leds_probe,

        .remove = leds_remove,

        .suspend = leds_suspend,

        .resume = leds_resume,

        .driver = {

                .name = DRIVER_NAME,

                .owner = THIS_MODULE,

        },

};

内核调用platform_driver_register注册硬件设备的时候,最终会调用到platform_driver结构里面的probe探测函数,iTOP-4412开发板的LED驱动里探测函数是leds_probe,定义如下:

static int leds_probe(struct platform_device *pdev)

{

        int ret, i;

        char *banner = "leds Initialize\n";

        printk(banner);

        for(i=0; iLED_NUM; i++)

        {

                ret = gpio_request(led_gpios[i], "LED");

                if (ret) {

                        printk("%s: request GPIO %d for LED failed, ret = %d\n", DRIVER_NAME,

                                        led_gpios[i], ret);

                        return ret;

                }

                s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);

                gpio_set_value(led_gpios[i], 1);

        }

        ret = misc_register(leds_dev);

        if(ret0)

        {

                printk("leds:register device failed!\n");

                goto exit;

        }

        return 0;

exit:

        misc_deregister(leds_dev);

        return ret;

}

在这个函数里会使用GPIO的操作函数来配置LED的两个GPIO引脚的功能为输出,默认输出高电平。控制LED的两个GPIO的定义在数组led_gpios中,如下:

static int led_gpios[] = {

        EXYNOS4_GPL2(0),

        EXYNOS4_GPK1(1),

};

接着回到LED的探测函数往下看,接着会调用misc_register向内核注册字符设备。misc_register函数传递的参数类型是miscdevice,miscdevice被称作杂项设备,嵌入式系统中用得比较多的一种设备驱动。在Linux内核的include/linux目录下有Miscdevice.h文件,要把自己定义的misc device从设备号定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,所有这些设备采用主编号10 ,一起归于misc device,其实misc_register就是用主标号10调用register_chrdev()的。iTOP-4412开发板的LED驱动里miscdevice的结构定义如下:

static struct miscdevice leds_dev = {

        .minor  = MISC_DYNAMIC_MINOR,

        .fops   = leds_ops,

        .name   = "leds",

};

从上面的定义可以看到minor次设备号定义成了MISC_DYNAMIC_MINOR,在misc子系统里如果此设备号定义成MISC_DYNAMIC_MINOR,那么在驱动注册的时候,内核会动态的为这个设备分配子设备号。LED驱动会在/devu录下创建设备节点leds。

驱动里面提供了设备文件的几个操作函数open,release,ioctl,上层应用首先调用open函数打开leds设备,然后调用ioctl来设置led的亮灭。leds_ioctl函数的实现如下所示:

long leds_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)

{

        printk("debug: leds_ioctl cmd is %d\n" , cmd);

        switch(cmd)

        {

                case 0:

                case 1:

                        if (arg  LED_NUM) {

                                return -EINVAL;

                        }

                        gpio_set_value(led_gpios[arg], cmd);

                        break;

                default:

                        return -EINVAL;

        }

        return 0;

}

通过上面的代码,可以知道上层应用使用ioctl,需要传递两个参数cmd和arg,cmd是led的状态(0是灭,1是亮),arg是代表操作哪个led。

Linux驱动程序有几种加载方式?以及它们之间的区别?

对呀!就静态加载和动态加载,静态加载是系统启动的时候由内核自动加载的,这个要事先将驱动编译进内核才行,还有一种就是动态加载,也就是模块加载方式,这种方式下驱动以模块的形式存放在文件系统中,需要时动态载入内核,这种主要用在调试的时候,比较方便灵活。

linux 下platform设备和驱动注册的先后顺序

Linux关于总线、设备、驱动的注册顺序

设备挂接到总线上时,与总线上的所有驱动进行匹配(用bus_type.match进行匹配),

如果匹配成功,则调用bus_type.probe或者driver.probe初始化该设备,挂接到总线上

如果匹配失败,则只是将该设备挂接到总线上。

驱动挂接到总线上时,与总线上的所有设备进行匹配(用bus_type.match进行匹配),

如果匹配成功,则调用bus_type.probe或者driver.probe初始化该设备;挂接到总线上

如果匹配失败,则只是将该驱动挂接到总线上。

需要重点关注的是总线的匹配函数match(),驱动的初始化函数probe()

1. platform_bus_type--总线先被kenrel注册。

2. 系统初始化过程中调用platform_add_devices或者platform_device_register,将平台设备(platform devices)注册到平台总线中(platform_bus_type)

3. 平台驱动(platform driver)与平台设备(platform device)的关联是在platform_driver_register或者driver_register中实现,一般这个函数在驱动的初始化过程调用。

通过这三步,就将平台总线,设备,驱动关联起来。

1. platform bus先被kenrel注册。

------------------------------------------------------

do_basic_setup() ---driver_init() ---platform_bus_init()--bus_register()

2. 系统初始化过程中调用platform_add_devices或者platform_device_register,将平台设备(platform devices)注册到平台总线中(platform_bus_type)

------------------------------------------------------

系统启动阶段,总线的驱动链表还是空的,所以启动阶段的platform_add_devices()只负责将设备添加到总线的设备链表上。

欢迎 发表评论:

文章目录
    搜索