DTS
general
http://bbs.elecfans.com/jishu_454965_1_1.html
GPIO :http://blog.youkuaiyun.com/luyejie8888/article/details/38172705 (shell命令控制)
http://bbs.ednchina.com/BLOG_ARTICLE_3027425.HTM (dts配置)
I2C :http://blog.youkuaiyun.com/trbbadboy/article/details/17302357 (python)
http://bbs.elecfans.com/jishu_454960_1_1.html (shell命令控制)
http://stackoverflow.com/questions/24834174/enabling-i2c1-on-beaglebone-black-using-dtb (dts配置)
SPI : http://www.cnblogs.com/dolphi/p/3662297.html (dts配置)
设备关键结构
struct bus_type {
char * name;
struct subsystem subsys;
struct kset drivers;
struct kset devices;
int (*match)(struct device *dev, structdevice_driver *drv);
struct device *(*add)(struct device * parent,char * bus_id);
int (*hotplug) (struct device *dev, char**envp, int num_envp, char *buffer, intbuffer_size);
/* Some fields omitted */ };
struct bus_type ldd_bus_type = {
.name = "ldd",
.match = ldd_match,
.hotplug = ldd_hotplug, };
bus_register(&ldd_bus_type);
match方法
一个新设备或者驱动被添加给这个总线. 它应当返回一个非零值如果给定的设备可被给定的驱动处理。
hotplug方法
允许总线在产生一个热插拔事件在用户空间之前,添加变量到环境中。
struct device {
struct device * parent;
struct kobject kobj;
char bus_id[BUS_ID_SIZE];
struct bus_type * bus;
struct device_driver * driver;
void * driver_data;
void (*release)(struct device *dev);
/* Several fields omitted */ };
struct device ldd_bus = {
.bus_id = "ldd0",
.release = ldd_bus_release
};
device_register(&ldd_bus); 注册总线
struct ldd_device { 继承device
char * name;
struct ldd_driver * driver;
struct device dev;
};
int register_ldd_device(struct ldd_device *ldddev) belong to bus?什么时候调用?
ldddev->dev.bus = &ldd_bus_type;
ldddev->dev.parent = &ldd_bus;
ldddev->dev.release =ldd_dev_release;
strncpy(ldddev->dev.bus_id,ldddev->name, BUS_ID_SIZE);
returndevice_register(&ldddev->dev); 注册设备
struct device_driver {
char * name;
struct bus_type * bus;
struct kobject kobj;
struct list_head devices;
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown) (struct device *dev);
};
struct ldd_driver{
char *version;
struct module *module;
struct device_driverdriver; 继承device_driver
struct driver_attribute version_attr;
};
static struct ldd_driver sculld_driver = {
.version = "$Revision:1.1$",
.module = THIS_MODULE,
.driver = { .name ="sculld", }, };
设备添加过程
struct bus_type pci_bus_type= {
.name = "pci",
.match = pci_bus_match,
.uevent = pci_uevent,
.probe = pci_device_probe,
.remove = pci_device_remove,
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
.bus_attrs = pci_bus_attrs,
.pm = PCI_PM_OPS_PTR,
};
Step1:PCI 子系统通过对 bus_register 的调用被加载入内核时. 当这个发生时, 驱动核心创建一个 sysfs 目录在/sys/bus/pci 里,它包含2 个目录:devices 和drivers.
struct pci_driver{
structlist_head node;
constchar *name;
conststruct pci_device_id *id_table;
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id);
void(*remove) (struct pci_dev *dev);
int (*suspend) (struct pci_dev *dev, pm_message_tstate);
int (*suspend_late) (struct pci_dev *dev,pm_message_t state);
int (*resume_early) (struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */
void(*shutdown) (struct pci_dev *dev);
structpci_error_handlers *err_handler;
structdevice_driver driver;
structpci_dynids dynids;
};
Step2:pci_driver被 PCI 核心初始化,当 PCI 驱动被注册时
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
drv->driver.probe = pci_device_probe;
drv->driver.remove =pci_device_remove;
drv->driver.kobj.ktype =&pci_driver_kobj_type;
Step3:PCI 核心注册 PCI 驱动到驱动核心
driver_register(&drv->driver);
Step4:查找所有的 PCI 设备. 当一个 PCI 设备被发现, PCI 核心在内存中创建一个 struct pci_dev 类型的新变量
struct pci_dev {
/* ... */
unsigned int devfn;
unsigned short vendor;
unsigned short device;
unsigned short subsystem_vendor;
unsigned short subsystem_device;
unsigned int class;
/* ... */
struct pci_driver *driver;
/* ... */
struct device dev;
/* ... */
};
Step5:在 PCI 设备结构被初始化之后, 设备被注册到驱动核心
device_register(&dev->dev);
Step6:设备接着被添加到所有设备的总线特定的列表中,接着注册到这个总线的所有驱动的列表被检查,并且总线的匹配功能被调用给每个驱动,指定这个设备(对于pci_bus_type 总线,匹配函数被PCI 核心设定为指向pci_bus_match 函数)
Step7:如果这个 probe 函数能够认领这个设备, 它做所有的需要的初始化来正确处理这个设备,驱动核心来添加设备到当前被这个特定驱动所绑定的所有设备列表,并且创建一个符号连接到这个它现在控制的设备
网络驱动
Struct net_device {
全局信息
名称、状态、链表节点、初始化函数指针
硬件信息
内存信息、I/O基址、中断、dma
接口信息
具体接口类型(如ether)相关信息
设备方法
Open/close……
}
创建net_device
net_device *alloc_netdev(intsizeof_priv, const char *name, void( *setup)(net_device *))
net_device初始化
ether_setup(dev); 以太网相关的缺省初始化
dev->open = snull_open; 网络设备的处理函数
dev->stop = snull_release;
……
priv私有数据
netdev_priv(dev)
注册网络设备
int register_netdev(net_device*dev)
模块卸载
unregister_netdev(snull_devs[i]); 从系统中删除接口
snull_teardown_pool(snull_devs[i]); 接口内部数据清理
free_netdev(snull_devs[i]); 释放接口
设备方法
打开设备
获取硬件地址 memcpy(dev->dev_addr,"\0SNUL0", ETH_ALEN);
启动发送队列 netif_start_queue
发送
*hard_start_xmit将sk_buff中的数据通过底层接口发送
发送并发
xmit_lock
netif_stop_queue、netif_wake_queue
netif_tx_disable
发送超时
*tx_timeout
发散、汇聚
避免拷贝,使用Frags数组
接收
创建sk_buff保存报文内容
netif_rx
接收缓解
NAPI
中断处理
Priv->lock
根据priv->status区分发送、接收
连接状态
Socket缓存
BOOT代码
eth_initialize
Linux代码
drivers/net/ethernet/ti
drivers/net/phy
内存映射
Kobject
kobject_add
kobject_add_internal
parent= kobject_get(kobj->parent);
kobj_kset_join(kobj); //kset添加kobject
list_add_tail(&kobj->entry,&kobj->kset->list);
create_dir(kobj); //sysfs添加kobject目录
sysfs_create_dir_ns(kobj, kobject_namespace(kobj));
populate_dir(kobj); //sysfs添加kobject属性文件
sysfs_create_file(kobj, attr);
kobj_child_ns_ops(kobj);
sysfs_enable_ns(kobj->sd);
kobj_type {
void (*release)(struct kobject *);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
struct sysfs_ops {
ssize_t (*show)(structkobject *kobj, struct attribute *attr, char *buffer);
ssize_t (*store)(structkobject *kobj, struct attribute *attr, const char *buffer, size_t size);
};
attribute {
char *name; //属性的名字( 如同它出现在kobject 的sysfs 目录中)
struct module *owner; //一个指向模块的指针(如果有一个), 模块负责这个属性的实现
mode_t mode;
};
非缺省属性
int sysfs_create_file(structkobject *kobj, struct attribute *attr);
struct bin_attribute {
struct attribute attr;
size_t size;
ssize_t (*read)(struct kobject*kobj, char *buffer, loff_t pos, size_t size);
ssize_t (*write)(struct kobject*kobj, char *buffer, loff_t pos, size_t size);
};
int sysfs_create_bin_file(struct kobject *kobj, structbin_attribute *attr);
热插拔事件
热插拔事件转变为一个对/sbin/hotplug 的调用,它响应每个事件,通过加载驱动,创建
设备节点,安装分区,或者采取任何其他的合适的动作.
kset_hotplug_ops {
int (*filter)(structkset *kset, struct kobject *kobj);
char *(*name)(struct kset *kset, structkobject *kobj);
int (*hotplug)(structkset *kset, struct kobject *kobj, char **envp, int num_envp, char *buffer, intbuffer_size);
};
bus_type {
char *name;
struct subsystemsubsys;
struct kset drivers;
struct kset devices;
int (*match)(structdevice *dev, struct device_driver *drv);
struct device*(*add)(struct device * parent, char * bus_id);
int (*hotplug)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
/* Some fieldsomitted */
};
int bus_register(struct bus_type *bus)
int (*match)
(struct device *device, struct device_driver *driver);
无论何时一个新设备或者驱动被添加给这个总线,这个方法被调用.
int (*hotplug)
(struct device *device, char **envp, int num_env p, char*buffer, int buffer_size)
在产生一个热插拔事件在用户空间之前,允许总线添加变量到环境中.
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);
};
int bus_create_file(structbus_type *bus, struct bus_attribute *attr);
device {
struct device *parent;
struct kobject kobj;
char bus_id[BUS_ID_SIZE];
struct bus_type *bus;
struct device_driver *driver;
void *driver_data;
void (*release)(structdevice *dev);
/* Several fields omitted */
};
最少, parent, bus_id, bus, 和release 成员必须在设备结构被注册前设置.
int device_register(struct device *dev);
device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, char*buf);
ssize_t (*store)(struct device *dev, constchar *buf, size_t count);
};
int device_create_file(structdevice *device, struct device_attribute *entry);
device_driver {
char *name;
struct bus_type *bus;
struct kobject kobj;
struct list_head devices;
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown) (struct device *dev);
};
int driver_register(struct device_driver *drv);
struct class {
char *name;
struct class_attribute *class_attrs;
struct class_device_attribute*class_dev_attrs;
int (*hotplug)(struct class_device *dev, char**envp, int num_envp, char *buffer, int buffer_size);
void (*release)(struct class_device *dev);
void (*class_release)(struct class *class);
/* Some fields omitted */
};
int class_register(struct class *cls);
class_device {
struct kobject kobj;
struct class *class;
struct device *dev;
void *class_data;
char class_id[BUS_ID_SIZE];
};
class_interface {
struct class *class;
int (*add) (struct class_device *cd);
void (*remove) (struct class_device *cd);
};