码迷,mamicode.com
首页 > 其他好文 > 详细

kernel something

时间:2016-06-12 02:48:56      阅读:198      评论:0      收藏:0      [点我收藏+]

标签:

在dts文件中:

#include "../../mach-rtk119x/include/mach/memory.h"                /* 包含一个.h */

/* /memreserve/保留内存,SYS_BOOTCODE_MEMBASE表示bootcode内存起始地址,SYS_BOOTCODE_MEMSIZE表示大小 */
/memreserve/ SYS_BOOTCODE_MEMBASE       SYS_BOOTCODE_MEMSIZE;                           /* ROM code,bootcode */
/memreserve/ PLAT_NOR_BASE_PHYS         PLAT_NOR_SIZE;                                          /* NOR */
/memreserve/ PLAT_AUDIO_BASE_PHYS       PLAT_AUDIO_SIZE;                                        /* Audio Firmware */
/memreserve/ RPC_RINGBUF_PHYS           RPC_RINGBUF_SIZE;                                       /* RPC ring buffer */
/memreserve/ PLAT_SECURE_BASE_PHYS PLAT_SECURE_SIZE; /* SECURE */
/memreserve/ ION_AUDIO_HEAP_PHYS    ION_AUDIO_HEAP_SIZE;
/memreserve/ ION_MEDIA_HEAP_PHYS1   ION_MEDIA_HEAP_SIZE1;
/memreserve/ ION_MEDIA_HEAP_PHYS2   ION_MEDIA_HEAP_SIZE2;
/memreserve/ ION_MEDIA_HEAP_PHYS3   ION_MEDIA_HEAP_SIZE3;
/memreserve/ ION_MEDIA_HEAP_PHYS4   ION_MEDIA_HEAP_SIZE4;
/* 包含其他dts文件 */
/include/ "xxx-irT377.dtsi"
##################################################################################################################
##################################################################################################################
##################################################################################################################

原子操作,自旋锁:忙等待(所以要快),互斥锁:睡眠等待。信号量是同时限数量进入:睡等待。
自旋锁:spin_lock、spin_lock_irq 和 spin_lock_irqsave 对应 spin_unlock, spin_unlock_irq ,spin_unlock_irqrestore
spin_lock锁住,中断可以打断。不要在中断中使用spin_lock。如果线程进入临界区,此时中断-将忙等待自旋锁,对导致死锁。除非线程和中断刚好是2个CPU在处理
spin_lock_irq禁止本地中断,然后锁住。spin_unlock_irq 解锁,打开中断
spin_lock_irqsave保存中断状态,禁止本地中断,锁住。spin_unlock_irqrestore解锁,恢复中断状态,不一定是开。
原子操作
atomic_t
互斥锁:
struct mutex数据类型,mutex_lock(struct mutex *lock)和mutex_unlock(struct mutex *lock)是加锁和解锁
互斥量:同步。
struct semaphore数据类型,down(struct semaphore * sem)和up(struct semaphore * sem)
API函数定义     功能说明
DECLARE_RWSEM(name)     声明名为name的读写信号量,并初始化它。
void init_rwsem(struct rw_semaphore *sem);     对读写信号量sem进行初始化。
void down_read(struct rw_semaphore *sem);     读者用来获取sem,若没获得时,则调用者睡眠等待。
void up_read(struct rw_semaphore *sem);     读者释放sem。
int down_read_trylock(struct rw_semaphore *sem);     读者尝试获取sem,如果获得返回1,如果没有获得返回0。可在中断上下文使用。
void down_write(struct rw_semaphore *sem);     写者用来获取sem,若没获得时,则调用者睡眠等待。
int down_write_trylock(struct rw_semaphore *sem);     写者尝试获取sem,如果获得返回1,如果没有获得返回0。可在中断上下文使用
void up_write(struct rw_semaphore *sem);     写者释放sem。
void downgrade_write(struct rw_semaphore *sem);     把写者降级为读者。
锁与互斥量:一个是互斥,一个是同步(做到互斥);一个主要线程间(也能进程间),一个进程间;一个线程锁和解锁有效,一个进程间解锁;一个允许进入一个,一个允许进入多个。

##################################################################################################################
##################################################################################################################
##################################################################################################################

内存:
-----物理区--8M隔离--vmalloc区--8k隔离--4M的高端映射区--固定映射区--128k
-----保留区,专用区,高端区(可以一个地址映射多个物理地址),DMA,常规区。
buddy算法:假设内存分为10个块,一个块=8页(也就是8*4k=32k,每个块的页数也必须是2^n次方)。比如我们声请2页数据,
    他就回找到第一个空闲页(8*4k),分为2个空闲页(4+4),然后再分(2+2),拿2页分配内存。
    现在内存的结构就是 2*1(已用) 2*1(未用) 4*1(未用)  8*9(未用) 
看cat /proc/buddyinfo可以得到内存最大块是多少。

内存声请:
1.//1和2都是线性映射,虚拟和物理内存都连续。
void *kmalloc(size_t size, int flags)//声请连续空间,他其实不是用的buddy算法,因为buddy使用页为基础的。slab
------kfree释放-------
flags:GFP_KERNEL表示在内核空间声请。而GFP_KERNEL标志会可能等待满足条件,因此不要在中断使用。
    GFP_ATOMIC:如果没有空闲页,直接返回。
    GFP_USER:从用户空间分配内存(用来给用户的?),阻塞。
    GFP_HIGHUSER:从高端内存分配(用来给用户的?)
    GFP_DMA:从DMA空间分配
    GFP_NOIO:不允许任何io初始化。
    GFP_NOFS:不允许任何文件系统调用。
    __GFP_HIGHMEM:分配的内存 可以 在高端区。
    ....
2.
底层内存声请也是用2^n为单位的。
__get_free_pages(flags, order)//flags同上,order是声请2^order页的空间,order最大值1024,应该和/proc/buddyinfo最大块有关。
__free_pages(addr, order)
__get_free_page(flags) == __get_free_pages(flags, 0)
free_page(addr)
3.void *vmalloc(unsigned long size)//用的GFP_KERNEL标志。会做内存映射动作(非线性映射),小内存分配不建议使用。

slab:先用__get_free_pages以页为单位分配空间后再划分,节约空间。














##################################################################################################################
##################################################################################################################
##################################################################################################################
ion.c是android开发的内存分配。
内存页调整,意思就是如果addr不是页的整数倍,调整为整数倍。多余的内存不满一页用一页调整
#define PAGE_ALIGN(addr)    (((addr)+PAGE_SIZE-1)&PAGE_MASK)
#define PAGE_SHIFT 12
#define PAGE_SIZE   (1UL << PAGE_SHIFT)//一般一页大小为4k
#define PAGE_MASK   (~(PAGE_SIZE-1))

##################################################################################################################
int misc_register(struct miscdevice * misc)
注册一个杂项设备。调用:
    misc->this_device = device_create(misc_class, misc->parent, dev,
                      misc, "%s", misc->name);
misc_class是主设备好为10的集合,misc->parent=null,dev设备号,misc设备私有数据,misc->name设备名。

杂项设备注册时就在dev中创建了设备文件,设备文件的私有变量为misc杂项设备自己。


##################################################################################################################
##################################################################################################################
##################################################################################################################
红黑树结构:
struct rb_node {
    unsigned long  __rb_parent_color;//保存父地址和颜色
    struct rb_node *rb_right;
    struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));//对齐方式:4个字节:扩展http://blog.163.com/tyw_andy/blog/static/11679021200910635047652/
注意几点:
1.__rb_parent_color类型是unsigned long,说明地址用的这个类型。因为int4字节,long(跟随系统变的)在64为操作系统是8字节
2.__rb_parent_color如何保存地址加颜色。对齐方式决定的:aligned(sizeof(long)4个字节对齐,分配地址后2位必定是0,这根内存分配有点关系。
3.__rb_parent_color为第一个数据,所以rb_node首地址也是__rb_parent_color的地址,可以相互切换。
3.__attribute__使用地方很多,取消对齐,设置对齐方式等等。
rb_tree.c红黑树代码,rbtree_test.c是红黑树调用代码---rbtree_test_init---insert(先找到位置,插入,调整)---erase(先用合适的点来替换,然后调整)。其实跟AVL差不多。
参考http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html


##################################################################################################################
##################################################################################################################
##################################################################################################################
#define for_each_child_of_node(parent, child)是循环得到每一个设备树自节点。
//获得一个设备节点的int属性,device_node设备节点,propname节点对应的属性:如reg,out_value输出。返回0正常
static inline int of_property_read_u32(const struct device_node *np,
                       const char *propname,
                       u32 *out_value)
//获取一个设备节点-数组属性:device_node设备节点,name节点对应的属性:如reg,lenp得到长度。返回得到的内容为数组。
如:        my,ion {
                compatible = "my,myx-ion";
                #address-cells = <1>;
                #size-cells = <0>;
                my,memory-reserve = <
                0x12345678 0x100 0x0f
                >;
        };
中my,memory-reserve可以返回一个u32数组,3个数据
const void *of_get_property(const struct device_node *np, const char *name,
                int *lenp)
//以文件描述符为索引,关联当前进程描述符和 file 对象,为之后的 read 和 write 等操作作准备。
void fd_install(unsigned int fd, struct file *file)



##################################################################################################################
##################################################################################################################
##################################################################################################################
I2C核心:
i2c_add_numbered_adapter()添加一个已经指定了adap->nr适配器。adap->nr就是idr管理(id管理器,i2c有自己的i2c_adapter_idr管理器)。
i2c_register_adapter()上面的函数其实就是调用这个函数,注册到内核
dev_set_name(&adap->dev, "i2c-%d", adap->nr);设置名字,i2c-0...等等
sysfs_create_link()创建一个软链接。
device_create()在sys/device和sys/class创建一个目录,如果dev_t不为0,还会在/dev创建文件
device_create_file()用来为device_create创建的目录添加属性文件。
i2c注册过程:
因为i2c添加到了设备树,驱动注册就用这个了:
static int __init rtk_i2c_init_driver(void){
    return platform_driver_register(&rtk_i2c_driver);
}
subsys_initcall(rtk_i2c_init_driver);

static int rtk_i2c_probe(struct platform_device *pdev){

    struct rtk_i2c_dev *i2c_dev;
    struct clk *div_clk = NULL;
    void __iomem *base;
    int irq;
    u32 i2c_id;
    int ret = 0;
    /*提取设备树reg(如reg = <0x1801B700 0x400>;),将地址和空间映射出来*/
    base = of_iomap(pdev->dev.of_node, 0);
    /*初始化init_IRQ是会对interrupt-controller;建立irq域,因此这里用irq_of_parse_and_map通过interrupts<中断向量表里面的项>映射出内核的irq*/
    irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
    /*i2c-num属性就是用来定义本个i2c在总线的idr*/
    if(of_property_read_u32(pdev->dev.of_node, "i2c-num", &i2c_id)){}
    /*自定义的i2c结构体,放寄存器地址等。*/
    i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
    if (!i2c_dev) {}

    i2c_dev->base = base;
    i2c_dev->div_clk = div_clk;
    /*adapter就是我们要注册的i2c适配器,这里指定算法algorithm,而rtk_i2c_algo就是我们实际读写硬件的接口。*/
    i2c_dev->adapter.algo = &rtk_i2c_algo;
    i2c_dev->irq = irq;
    i2c_dev->cont_id = pdev->id;
    i2c_dev->id = i2c_id;
    i2c_dev->dev = &pdev->dev;

    ret = of_property_read_u32(i2c_dev->dev->of_node, "clock-frequency",
                    &i2c_dev->bus_clk_rate);
    if (ret)
        i2c_dev->bus_clk_rate = 100000;
    /*设置platform私有数据---*/
    platform_set_drvdata(pdev, i2c_dev);
    /*对i2c硬件进行初始化。*/
    ret = rtk_i2c_init(i2c_dev);
    /*中断处理函数,i2c_dev->irq是映射出来的中断号。*/
    ret = devm_request_irq(&pdev->dev, i2c_dev->irq, venus_i2c_isr, IRQF_SHARED, dev_name(&pdev->dev), (void*)i2c_dev->p_this);
    /*设置适配器的数据。*/
    i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
    i2c_dev->adapter.owner = THIS_MODULE;
    i2c_dev->adapter.class = I2C_CLASS_HWMON;
    strlcpy(i2c_dev->adapter.name, "Realtek I2C adapter", sizeof(i2c_dev->adapter.name));
    i2c_dev->adapter.dev.parent = &pdev->dev;
    /*指定nr,在注册适配器到内核的时候,通过nr作为idr(id管理器)来管理的。*/
    i2c_dev->adapter.nr = pdev->id;
    i2c_dev->adapter.dev.of_node = pdev->dev.of_node;
    /*----调用i2c_add_adapter----这里就是指定nr注册适配器,添加adapter(adap->dev.bus = &i2c_bus_type;)到i2c_bus_type总线去*/
    ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
    if (ret) {
        dev_err(&pdev->dev, "Failed to add I2C adapter\n");
        return ret;
    }
    /*----调用i2c_new_device----源码感觉里面是向系统注册的i2c_client,我不太明白?因为我们看后面i2c_dev.c*/
    of_i2c_register_devices(&i2c_dev->adapter);

    return 0;
}
i2c_dev.c:
static int __init i2c_dev_init(void)
{
    在kernel/drivers/i2c里面有的,
    /*注册了字符设备,主设备号89,方法也是给出了。*/
    res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
    /*对在总线i2c_bus_type上的每一个i2c设备调用i2cdev_attach_adapter----device_create---/dev/i2c-0等设备*/
    i2c_for_each_dev(NULL, i2cdev_attach_adapter);
}
打开方法:通过inode的从设备好得到对应的i2c设备和适配器,创建一个i2c_client *client,用于找到对应的adapter,配置访问设备地址,读写等.
static int i2cdev_open(struct inode *inode, struct file *file)
/*控制方法:对i2c_client *client进行配置,以及读写i2c数据i2cdev_ioctl_rdrw----i2c_transfer----adap->algo->master_xfer就是上面注册的rtk_i2c_algo*/
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)


##################################################################################################################
##################################################################################################################
##################################################################################################################
解析ion:
入口函数,
static int rtk_ion_probe(struct platform_device *pdev)
{
    ....
    /*解析设备树,ion设备有7个子节点,提取每个子节点的reg(id号),初始化每一个pdata*/
    pdata = rtk_ion_parse_dt(pdev->dev.of_node);
    ....
    /*创建ion杂项设备,提供读写方法*/
    rtk_phoenix_ion_device = ion_device_create(rtk_phoenix_ion_ioctl);
    /*创建heap,heap在ion中提供了分配方法,在创建的时候会根据定义的分配类型注册对应的方法函数:如ion_cma_heap_create使用ion_cma_ops*/
    heaps[i] = ion_heap_create(heap_data);
    /*将创建的heap放到ion_device的heap链表中*/
    ion_device_add_heap(rtk_phoenix_ion_device, heaps[i]);
    /*设备platform设备的私有变量。    */
    platform_set_drvdata(pdev, rtk_phoenix_ion_device);
}
上面不是创建了ion设备么。现在就给他们分配内存,用户层用/dev/ion进行io控制
static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
....
    case ION_IOC_ALLOC:
    /*用户传进来的传输放到data里面,所以用户数据结构需要一致。len长度,align页对齐(分配的没有页整数倍,分整数倍),heap_id_mask和heap的id对应才分配,flags标志
        调用ion_alloc--ion_buffer_create--ion_cma_allocate(ion_heap_create注册的方法,假设用cma类型--ion_cma_ops)
    dma_alloc_coherent:分配内存空间,dev设备,len空间大小,分配后的物理地址,flags和kmalloc的flags一样。返回虚拟地址。
    info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle),
                        GFP_HIGHUSER | __GFP_ZERO);
        
    */
    handle = ion_alloc(client, data.allocation.len,
                        data.allocation.align,
                        data.allocation.heap_id_mask,
                        data.allocation.flags);
    
....
    case ION_IOC_PHYS:
    ....
    /*用来获取内存的物理地址和长度*/
    ret = ion_phys(client, handle, &addr, &len);
    ....
    case ION_IOC_SHARE:
    case ION_IOC_MAP:
        /*分配的内存搞个文件描述符,可供mmap使用*/
        data.fd.fd = ion_share_dma_buf_fd(client, handle);
....
    case ION_IOC_IMPORT:
        /*通过文件描述符和client得到对应handle*/
        handle = ion_import_dma_buf(client, data.fd.fd);
        

    .....
}


##################################################################################################################
##################################################################################################################
##################################################################################################################
设备,驱动,总线,类。
sysfs:是驱动的文件系统。
类:来自i2c-dev.c
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev")创建(注册)一个类,参数i2c-dev就是要创建类的名字,目录位于:/sys/class/i2c-dev
对应class_destroy(i2c_dev_class)
device_create就是在这个i2c_dev_class上创建设备目录,其实就是做一个链接/sys/devices/..,如果dev_t不为0,同时在dev里面创建设备文件。
//这里会创建3个文件:/sys/class/i2c-dev/i2c-x, /sys/devices/virtual/i2c-dev/i2c-x, /dev/i2c-x
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
                     MKDEV(I2C_MAJOR, adap->nr), NULL,
                     "i2c-%d", adap->nr);

dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip,
                "gpiochip%d", chip->base);//这里使用了设备号为0,这里就不会在dev目录下创建设备文件了。但是sys/class/目录还是有对应的目录和文件。

出自gpiolib.c:
status = class_register(&gpio_class);和class_create差不多,class_create会分配class,然后调用class_register。
对应class_unregister
//设备的使用  DEVICE_ATTR  ,对总线使用  BUS_ATTR  ,对驱动使用 DRIVER_ATTR  ,对类别 (class) 使用  CLASS_ATTR
//原型:#define DEVICE_ATTR(_name, _mode, _show, _store) struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
static struct class_attribute gpio_class_attrs[] = {//这是类属性,在类目录创建export和unexport文件。用于和用户层通讯。
    __ATTR(export, 0200, NULL, export_store),//__ATTR在sysfs.h定义,用来设置属性的名字,读写权限,文件读时函数,写函数。
    __ATTR(unexport, 0200, NULL, unexport_store),
    __ATTR_NULL,
};

static struct class gpio_class = {
    .name =        "gpio",//类的目录:/sys/class/gpio
    .owner =    THIS_MODULE,
    .class_attrs =    gpio_class_attrs,
};

status = sysfs_create_group(&dev->kobj,
        &gpiochip_attr_group);//对设备目录添加一个属性组,也就是这个设备目录如:/sys/devices/virtual/gpio/gpiochip0中添加多个属性文件,供用户配置的
static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL);//就是定义一个设备属性dev_attr_ngpio,属性跟class_atrribute一样。
static const struct attribute *gpiochip_attrs[] = {
    &dev_attr_base.attr,//可以读取gpio基地址
    &dev_attr_label.attr,//读取标签
    &dev_attr_ngpio.attr,//数量
    NULL,
};
static const struct attribute_group gpiochip_attr_group = {
    .attrs = (struct attribute **) gpiochip_attrs,
};

status = device_create_file(dev, &dev_attr_direction);//调用sysfs_create_file为一个sys/class/中一个设备目录创建一个属性文件。
int sysfs_create_dir(struct kobject * kobj)//创建目录
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)//为目录创建一个属性


我们看看gpiolib.c中gpiolib_sysfs_init,gpio文件系统初始化:
##################################################################################################################
static int __init gpiolib_sysfs_init(void)
{
    int        status;
    unsigned long    flags;
    struct gpio_chip *chip;

    status = class_register(&gpio_class);//注册类,在/sys/class创建目录。
    if (status < 0)
        return status;

    /* Scan and register the gpio_chips which registered very
     * early (e.g. before the class_register above was called).
     *
     * We run before arch_initcall() so chip->dev nodes can have
     * registered, and so arch_initcall() can always gpio_export().
     */
    spin_lock_irqsave(&gpio_lock, flags);
    list_for_each_entry(chip, &gpio_chips, list) {//在gpio驱动注册的时候,有注册gpio_chip(包含多个gpio口),保存在gpio_chips,这里就是对每组gpio进行sys目录创建。
        if (!chip || chip->exported)
            continue;
        //chip是一组gpio控制。
        spin_unlock_irqrestore(&gpio_lock, flags);
        status = gpiochip_export(chip);//把gpiochip导出来,意思就是用户可见,看下面函数知道,在/sys/class和/sys/devices显示出来。
        spin_lock_irqsave(&gpio_lock, flags);
    }
    spin_unlock_irqrestore(&gpio_lock, flags);


    return status;
}
static int gpiochip_export(struct gpio_chip *chip)
{
    int        status;
    struct device    *dev;

    /* Many systems register gpio chips for SOC support very early,
     * before driver model support is available.  In those cases we
     * export this later, in gpiolib_sysfs_init() ... here we just
     * verify that _some_ field of gpio_class got initialized.
     */
    if (!gpio_class.p)
        return 0;

    /* use chip->base for the ID; it‘s already known to be unique */
    mutex_lock(&sysfs_lock);
    dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip,
                "gpiochip%d", chip->base);//创建/sys/class/gpio/gpiochipx(链接文件)和/sys/devices/virtual/gpio/gpiochipx
    if (!IS_ERR(dev)) {//创建成功
        status = sysfs_create_group(&dev->kobj,
                &gpiochip_attr_group);//对/sys/devices/virtual/gpio/gpiochipx/目录创建属性文件。
    } else
        status = PTR_ERR(dev);
    chip->exported = (status == 0);
    mutex_unlock(&sysfs_lock);
    if (status) {//失败
        unsigned long    flags;
        unsigned    gpio;

        spin_lock_irqsave(&gpio_lock, flags);
        gpio = 0;
        while (gpio < chip->ngpio)
            chip->desc[gpio++].chip = NULL;
        spin_unlock_irqrestore(&gpio_lock, flags);

        pr_debug("%s: chip %s status %d\n", __func__,
                chip->label, status);
    }

    return status;
}


##################################################################################################################
##################################################################################################################
##################################################################################################################
事件通知链:有一个链表,注册了很多回调函数,等有个事件发生时,通过优先级执行注册了的所有回调函数。
linux子系统可能都有一定关系,一个子系统发生了变化,就需要通知到其他子系统,这个通知链就好用了。
参考:
http://blog.csdn.net/wuhzossibility/article/details/8079025

struct notifier_block {
    notifier_fn_t notifier_call;//事件通知时的回调函数。
    struct notifier_block __rcu *next;
    int priority;//优先级
};
通知块的定义。

int register_netdevice_notifier(struct notifier_block *nb)
err = raw_notifier_chain_register(&netdev_chain, nb);
向netdev_chain通知链注册一个通知块nb

int unregister_netdevice_notifier(struct notifier_block *nb)
err = raw_notifier_chain_unregister(&netdev_chain, nb);
注销一个通知块。

int call_netdevice_notifiers(unsigned long val, struct net_device *dev)
{
    ASSERT_RTNL();
    return raw_notifier_call_chain(&netdev_chain, val, dev);
}
这个就是向通知链发送事件。

网络设备事件通知:netdevice.h
#define NETDEV_UP    0x0001    /* For now you can‘t veto a device up/down */
#define NETDEV_DOWN    0x0002
#define NETDEV_REBOOT    0x0003    /* Tell a protocol stack a network interface
........

usb通知链表头为usb_notifier_list
在/drivers/usb/core/Notify.c文件中,有四个函数()对usb_notifier_list中发送通知,这四个函数如下:
usb_notify_add_device    //有设备添加
usb_notify_remove_device    //有设备移除
usb_notify_add_bus    //总线添加
usb_notify_remove_bus    //总线移除
##################################################################################################################
##################################################################################################################
##################################################################################################################
udev:是一个应用层源码包。
kobject_uevent(&drv->p->kobj, KOBJ_ADD);通知udev
##################################################################################################################
##################################################################################################################
##################################################################################################################
lsusb:命令,查看系统usb设备
Bus 003 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120
Bus 003 Device 004: ID 1bcf:2c55 Sunplus Innovation Technology Inc.
Bus 003 Device 003: ID 093a:2510 Pixart Imaging, Inc. Optical Mouse
Bus 003 Device 002: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 003 Device 006: ID 8087:07da Intel Corp.
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
bus表示总线号(系统有多个usb总线),Device表示设备编号,001表示root hub,002串口:表示对新添的设备进行编号(比如003是鼠标,我拔掉鼠标,然后再插上为007)。
ID:usb_device_descriptor.idVendor和usb_device_descriptor.idProduct从设备读取(厂家设定)。

lsusb参数:
-D 设备路径 不扫描/proc/bus/usb,而以指定的设备路径取代
-p 内核路径 使用其他USB设备在内核的路径,默认为/proc/bus/usb
-t 将USB设备以树状架构输出
-v 列出较详细的运行过程
-vv 列出完整的运行过程
-V 显示版本信息
lsusb -v:读取5个结构体。usb_device_descriptor,usb_config_descriptor,usb_interface_descriptor,usb_endpoint_descriptor,usb_string_descriptor
主控制器驱动:
OHCI:open host control interface.
UHCI:universal host control interface通用
EHCI:enhanced host control interface增强
xHCI:extensible可扩展,支持3.0和2.0

URB:usb request block

usb子系统入口:static int __init usb_init(void)
http://blog.csdn.net/orz415678659/article/details/8444999

读完usb_init之后,它就注册了usb总线,注册了usbfs驱动,hub总线集线器(开了个线程,检测usb热插拔等事件的),一个通用设备驱动。

而我使用lsusb可以看到bus有001,002,003,在/dev/bus/usb/也能看到。所以这里只是usb系统初始化,其他内容在哪里呢?usb_devio_init->USB_DEVICE_MAX->USB_MAXBUS,感觉bus可以有多个
跟一下USB_MAXBUS在hcd.c有用到,hcd.c就是主控制器。
主控制器:
static int __init ehci_rtk_init(void)
{
    if (usb_disabled())
        return -ENODEV;
    pr_info("%s: " DRIVER_DESC "\n", hcd_name);
    //为ehci主控制器提供echi属性的driver:ehci_hc_driver
    ehci_init_driver(&ehci_rtk_hc_driver, NULL);
    return platform_driver_register(&ehci_rtk_driver);
}
static int ehci_rtk_drv_probe(struct platform_device *pdev)
{
...
    //ehci->caps主控制器寄存器地址,因为ehci的控制函数run,start,urb_enqueue(4种方式:控制,批量,中断,等时)都是操作寄存器实现的.ehci有通用的协议实现,所以这样做.
    hcd->rsrc_start = res.start;
    hcd->rsrc_len = resource_size(&res);
    hcd->regs = regs;
    ehci = hcd_to_ehci(hcd);
    ehci->caps = hcd->regs;
..
    //创建一个主控制器,
    hcd = usb_create_hcd(&ehci_rtk_hc_driver,
        &pdev->dev, dev_name(&pdev->dev));
...
    //添加主控制器,这里会对主控制器进行编号,就是bus的编号了.
    err = usb_add_hcd(hcd, irq, IRQF_SHARED);
...
}
在ehci_hc_driver中.irq=ehci_irq(也可能是用定时器来做的),中断会usb_hcd_poll_rh_status(hcd);通知hub线程出来中断信息,如热插拔事件,发送给hub线程--->hub_port_connect_change(hub, i,portstatus, portchange);--->status = usb_new_device(udev);--->创建一个设备,匹配不同的driver.如u盘和usbfs驱动匹配,做usbfs初始化.完成u盘挂载等.


##################################################################################################################
##################################################################################################################
##################################################################################################################

kobject:设备基类,sysfs就是通过它为基础来建立层次目录的。/sys/bus /sys/dev等。object对应一个目录(并不是每个都创建),sysfs_create_dir;sysfs_create_file为他创建属性
kset:是kobject的容器,也可以说管理吧。/sys/bus其实就是建立的kset。其中list是子object链表,kobj是自身的描述-子kobj的父kobj就是指向他。list_lock操作链表的锁,uevent_ops发消息的
int __init buses_init(void)//就是bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);创建kset集合,并创建/sys/bus目录。
retval = kset_register(&priv->subsys);//kset_create_and_add也是分配空间后,在调用他的。
int bus_register(struct bus_type *bus)中,

kernel something

标签:

原文地址:http://blog.csdn.net/hxchuan000/article/details/51613897

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!