最近在学习Linux设备模型,前面几篇文章也是读这篇的时候遇到问题,然后为了搞清楚先转去摸索才写出来的。
当然了,刚开始是先读到《Linux那些事儿之我是Sysfs》,搞不清楚才去读的《Linux设备模型浅析之uevent》,结果那篇文章说要先读《Linux设备模型浅析之设备篇》。
而读《Linux那些事儿之我是Sysfs》之前是先读了《Linux那些事儿之我是U盘》,原因是当初在某培训机构培训后有一家公司去招聘,面试唯一的问题是,你搞过USB驱动吗?答:没搞过。然后面试就结束了。
另一个原因是自己买了一个USB无线网卡,用Linux内核带的驱动该网卡很不稳定,然后就跑到某国内操作系统论坛抱怨Linux下上网速度很慢,后来发现编译安装官方给的驱动就可以正常运行(发现自己的抱怨真的很笨)。为了搞懂这些都是为什么,于是决定学习USB驱动,并准备接下来学习网卡驱动。
USB的学习,这里必须推荐一下《圈圈教你玩USB》,我买了它的书和开发板之后,才能够快速的入门USB协议。看完了这本书之后才会有耐心开始看《Linux那些事儿之我是U盘》,然后才终于打开了Linux内核学习的大门。
好了,上面是闲扯,也是自己这一个月能够入门Linux内核的历程,结束了上面的闲扯之后,下面接着闲扯。
网上的这篇《Linux设备模型浅析之设备篇》我看2011年已经在网上传了,估计内核版本也比较老了,文章给出的 http://zhiqiang0071.cublog.cn 也打不开了。
我是升级控,把涉及到的代码用新版本的内核源码贴出来,也是为了保证自己学习的时候能够深入进去。
现在www.kernel.org最新稳定版本为3.14.5,这里源码就用这个内核版本
文件/arch/arm/plat-samsung/devs.c
static struct resource s3c_rtc_resource[] = {
[0] = DEFINE_RES_MEM(S3C24XX_PA_RTC, SZ_256),
[1] = DEFINE_RES_IRQ(IRQ_RTC),
[2] = DEFINE_RES_IRQ(IRQ_TICK),
};
文件/include/linux/ioport.h
#define DEFINE_RES_MEM(_start, _size) \
DEFINE_RES_MEM_NAMED((_start), (_size), NULL)
#define DEFINE_RES_MEM_NAMED(_start, _size, _name) \
DEFINE_RES_NAMED((_start), (_size), (_name), IORESOURCE_MEM)
#define DEFINE_RES_NAMED(_start, _size, _name, _flags)\
{ \
.start = (_start), \
.end = (_start) + (_size) - 1, \
.name = (_name), \
.flags = (_flags), \
}
#define DEFINE_RES_IRQ(_irq) \
DEFINE_RES_IRQ_NAMED((_irq), NULL)
#define DEFINE_RES_IRQ_NAMED(_irq, _name) \
DEFINE_RES_NAMED((_irq), 1, (_name), IORESOURCE_IRQ)
struct platform_device s3c_device_rtc = {
.name = "s3c2410-rtc",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_rtc_resource),
.resource = s3c_rtc_resource,
};
文件/arch/arm/mach-s3c64xx/mach-smdk6410.c
static struct platform_device *smdk6410_devices[] __initdata = {
#ifdef CONFIG_SMDK6410_SD_CH0
&s3c_device_hsmmc0,
#endif
#ifdef CONFIG_SMDK6410_SD_CH1
&s3c_device_hsmmc1,
#endif
&s3c_device_i2c0,
&s3c_device_i2c1,
&s3c_device_fb,
&s3c_device_ohci,
&samsung_device_pwm,
&s3c_device_usb_hsotg,
&s3c64xx_device_iisv4,
&samsung_device_keypad,
#ifdef CONFIG_REGULATOR
&smdk6410_b_pwr_5v,
#endif
&smdk6410_lcd_powerdev,
&smdk6410_smsc911x,
&s3c_device_adc,
&s3c_device_cfcon,
&s3c_device_rtc,
&s3c_device_ts,
&s3c_device_wdt,
};
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
文件/drivers/base/core.c
/**
* device_initialize - 初始化device结构
* @dev: device.
*
* 这里初始化其区域来为其他层使用该设备准备。
* 这里是device_register()的前一半, 如果是被那个方法调用的话,
* 可是它也可以被单独调用, so one may use @dev's fields. In particular,
* 尤其在调用本方法后可以使用get_device()/put_device()修改引用次数。
*
* 除了一些被明确赋值为其他值的项外,@dev的所有区域必须被调用方
* 初始化为0. 最简单的方法是使用kzalloc()来分配一个包含@dev的结构。
*
* 注意: 一旦你调用了该方法,就要使用put_device()来放弃你的引用,
* 而不是直接释放@dev。
*/
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev, -1);
}
文件/drivers/base/core.c
/**
* platform_device_add - 添加platform设备到设备层
* @pdev: 正在添加的platform设备
*
* 这是platform_device_register()的第二部分, though may be called
* separately _iff_ pdev was allocated by platform_device_alloc().
*/
int platform_device_add(struct platform_device *pdev)
{
int i, ret;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
pdev->dev.bus = &platform_bus_type;
switch (pdev->id) {
default:
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
break;
case PLATFORM_DEVID_NONE:
dev_set_name(&pdev->dev, "%s", pdev->name);
break;
case PLATFORM_DEVID_AUTO:
/*
* 自动分配设备ID。我们把他标记为这样,来保证我们可以记住它
* 必须被释放,而且我们追加一个后缀来避免命名空间显示ID冲突。
*/
ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
if (ret < 0)
goto err_out;
pdev->id = ret;
pdev->id_auto = true;
dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
break;
}
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = dev_name(&pdev->dev);
p = r->parent;
if (!p) {
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
dev_err(&pdev->dev, "failed to claim resource %d\n", i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
dev_name(&pdev->dev), dev_name(pdev->dev.parent));
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
if (pdev->id_auto) {
ida_simple_remove(&platform_devid_ida, pdev->id);
pdev->id = PLATFORM_DEVID_AUTO;
}
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
release_resource(r);
}
err_out:
return ret;
}