linux驱动模型 -- 总线_设备_设备驱动

linux驱动模型 -- 总线_设备_设备驱动
linux驱动模型 -- 总线_设备_设备驱动

1. 总线、设备和驱动

1.1 简单介绍

Linux设备模型中三个很重要的概念就是总线、设备和驱动,即bus,device和driver。它们分别对应的数据结构分别为struct bus_type,struct device和struct device_driver。

总线是处理器与一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连。在最底层,Linux系统中的每一个设备都用device结构的一个实例来表示。而驱动则是使总线上的设备能够完成它应该完成的功能。

在系统中有多种总线,如PCI总线、SCSI总线等。系统中的多个设备和驱动是通过总线让它们联系起来的。在bus_type中两个很重要的成员就是struct kset drivers和struct kset devices。它分别代表了连接在这个总线上的两个链,一个是设备链表,另一个则是设备驱动链表。也就是说,通过一个总线描述符,就可以找到挂载到这条总线上的设备,以及支持该总线的不同的设备驱动程序。

1.2 总线、设备与驱动的绑定

在系统启动时,它会对每种类型的总线创建一个描述符,并将使用该总线的设备链接到该总线描述符的devices链上来。也即是说在系统初始化时,它会扫描连接了哪些设备,并且为每个设备建立一个struce device变量,然后将该变量链接到这个设备所连接的总线的描述符上去。另一方面,每当加载了一个设备驱动,则系统也会准备一个struct device_driver 结构的变量,然后再将这个变量也链接到它所在总线的描述符的drivers链上去。

对于设备来说,在结构体struct device中有两个重要的成员,一个是struct bus_type *bus,另一个是struct device_driver *driver。bus成员就表示该设备是链接到哪一个总线上的,而driver成员就表示当前设备是由哪个驱动程序所驱动的。对于驱动程序来说,在结构体struct device_driver中也有两个成员,struct bus_type *bus和struct list_head devices,这里的bus 成员也是指向这个驱动是链接到哪个总线上的,而devices这个链表则是表示当前这个驱动程序可以去进行驱动的那些设备。一个驱动程序可以支持一个或多个设备,而一个设备则只会绑定给一个驱动程序。

对于device与device_driver之间建立联系的方式,主要有两种方式。第一种,在计算机启动的时候,总线开始扫描连接在其上的设备,为每个设备建立一个struct device变量并链接到该总线的devices链上,然后开始初始化不同的驱动程序,驱动程序到它所在的总线的devices链上去遍历每一个还没有被绑定给某个驱动的设备,然后再查看是否能够支持这种设备,如果它能够支持这种设备,则将这个设备与这个驱动联系起来。即,将这个设备的device变量加到驱动的devices链上,同时让struct device中的device_driver指向当前这个驱动。第二种则是热插拔。也即是在系统运行时插入了设备,此时内核会去查找在该bus链上注册了的device_driver,然后再将设备与驱动联系起来。设备与驱动根据什么规则联系起来,它们是如何被联系起来的代码我们将在后面的章节进行详细的描述。

1.3 PCI总线

PCI是一种在CPU与I/O设备之间进行高速数据传输的一种总线。有很多设备都是使用PCI总线的,网卡就是其中之一。我们在前面讲了那些总线、设备与驱动方面的知识,原因就在于网卡是连接到PCI总线上,所以PCI总线、网卡设备以及网卡驱动就成了我们研究网卡的一个很重要的线索,尤其是在网络的链路层部分。下图显示了在一个系统中PCI 设备的一个框图:

PCI子系统声明了一个bus_type结构,为pci_bus_type。它就是PCI总线的描述符。在这个变量上,链接了PCI设备以及支持PCI设备的驱动程序。

1.4 PCI设备与驱动

PCI设备通常由一组参数唯一地标识,它们被vendorID,deviceID和class nodes所标识,即设备厂商,型号等,这些参数保存在pci_device_id结构中。每个PCI设备都会被分配一个pci_dev变量,内核就用这个数据结构来表示一个PCI设备。

所有的PCI驱动程序都必须定义一个pci_driver结构变量,在该变量中包含了这个PCI驱动程序所提供的不同功能的函数,同时,在这个结构中也包含了一个device_driver结构,这个结构定义了PCI子系统与PCI设备之间的接口。在注册PCI驱动程序时,这个结构将被初始化,同时这个pci_driver变量会被链接到pci_bus_type中的驱动链上去。

在pci_driver中有一个成员struct pci_device_id *id_table,它列出了这个设备驱动程序所能够处理的所有PCI设备的ID值。

1.5 PCI设备与驱动的绑定过程

下面描述一下对于PCI设备与驱动绑定的过程。首先在系统启动的时候,PCI总线会去扫描连接到这个总线上的设备,同时为每一个设备建立一个pci_dev结构,在这个结构中有一个device成员,并将这些pci_dev结构链接到PCI总线描述符上的devices链。如下图所示:

第二步是当PCI驱动被加载时,pci_driver结构体将被初始化,这一过程在函数pci_register_driver中:

drv->driver.bus = &pci_bus_type;

drv->driver.probe = pci_device_probe;

最后会调用driver_register(&drv->driver)将这个PCI驱动挂载到总线描述符的驱动链上。同时在注册的过程中,会根据pci_driver中的id_table中的ID值去查看该驱动支持哪些设备,将这些设备挂载到pci_driver中的devices链中来。如下图所示:

对于不同的设备,可能驱动程序也不一样,因此,对于上图中的Dev3,可能就需要另外一个驱动程序来对其进行驱动。所以当加载了Dev3的驱动程序时,其示意图如下图所示:

上面这三个示意图就描述了总线、设备以及驱动在系统中是如何进行相互联系的。前面对于驱动注册这些函数的描述较为简单,因为网卡是一个PCI设备,因此在后面具体地讲到网卡注册时再来详细地讲解和PCI相关的注册等函数。

1.6 小结

本部分主要讲解了总线、设备以及驱动方面的一些知识,由于网卡是一个PCI设备,因此具体地讲到了一点PCI总线、PCI设备及相应的PCI驱动方面的知识,但是由于PCI 本身就是很大的一个子系统,因此这里不可能对其进行详细地讲解,在后面对网卡的分析中,将对网卡中涉及到的和PCI相关的部分进行讲解。

1 2. 网卡在PCI层的注册

2.1 数据结构

前面第一章讲了总线、设备以及驱动方面的关系,也讲到了大多数网卡设备实际上是一个PCI设备。因此,本章就讲解网卡设备在注册时是如何注册到PCI总线上去的。在这里,以Intel的E100网卡驱动进行讲解。

前面讲到每个PCI设备都由一组参数唯一地标识,这些参数保存在结构体pci_device_id中,如下所示:

struct pci_device_id {

2__u32 vendor, device; /* Vendor and device ID or

PCI_ANY_ID*/

3__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */

4__u32 class, class_mask; /* (class,subclass,prog-if) triplet */

5kernel_ulong_t driver_data; /* Data private to the driver */

6};

7每个PCI设备驱动都有一个pci_driver变量,它描述了一个PCI驱动的信息,如下所示:

struct pci_driver {

8struct list_head node;

9char *name;

10const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */

11int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */

12void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */

13int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */

14int (*suspend_late) (struct pci_dev *dev, pm_message_t state);

15int (*resume_early) (struct pci_dev *dev);

16int (*resume) (struct pci_dev *dev); /* Device woken up */

17int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); /* Enable wake event */

18void (*shutdown) (struct pci_dev *dev);

19

20struct pci_error_handlers *err_handler;

21struct device_driver driver;

22struct pci_dynids dynids;

23

24int multithread_probe;

25};

26每个PCI驱动中都有一个id_table成员变量,记录了当前这个驱动所能够进行驱动的那些设备的ID值。

对于E100网卡驱动来说,它的pci_driver变量定义为:

static struct pci_driver e100_driver = {

https://www.360docs.net/doc/9717685418.html, = DRV_NAME,

28.id_table = e100_id_table,

29.probe = e100_probe,

30.remove = __devexit_p(e100_remove),

31#ifdef CONFIG_PM

32/* Power Management hooks */

33.suspend = e100_suspend,

34.resume = e100_resume,

35#endif

36.shutdown = e100_shutdown,

37.err_handler = &e100_err_handler,

38};

39里面e100_id_table就表示该E100驱动所能够支持的PCI设备的ID号,其定义为:

#define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\

40PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \

41PCI_CLASS_NETWORK_ETHERNET << 8, 0xFFFF00, ich }

42static struct pci_device_id e100_id_table[] = {

43INTEL_8255X_ETHERNET_DEVICE(0x1029, 0),

44INTEL_8255X_ETHERNET_DEVICE(0x1030, 0),

45…

46{ 0, }

47};

48当PCI层检测到一个PCI设备能够被某PCI驱动所支持时(这是通过函数pci_match_one_device来进行检测的),就会调用这个PCI 驱动上的probe函数,在该函数中会对该特定的PCI设备进行一些具体的初始化等操作。比如对于E100设备驱动来说,其probe函数为e100_probe。在这个函数中,会对网卡设备进行初始化。

e100_probe主要就涉及到网卡设备net_device的初始化,我们现在先来关注一下从网卡注册一直到调用e100_probe这一个过程的整个流程。

2.2 E100初始化

E100驱动程序的初始化是在函数e100_init_module()中的,如下:

static int __init e100_init_module(void)

49{

50if(((1 << debug) - 1) & NETIF_MSG_DRV) {

51printk(KERN_INFO PFX "%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);

52printk(KERN_INFO PFX "%s\n", DRV_COPYRIGHT);

53}

54return pci_register_driver(&e100_driver);

55}

56在这个函数中,调用了pci_register_driver()函数,对e100_driver这个驱动进行注册。

2.3 PCI注册

在前面我们已经看到,PCI的注册就是将PCI驱动程序挂载到其所在的总线的drivers链,同时扫描PCI设备,将它能够进行驱动的设备挂载到driver上的devices链表上来,这里,我们将详细地查看这整个流程的函数调用关系。

pci_register_driver()->__pci_register_driver()

/**

57* __pci_register_driver - register a new pci driver

58* @drv: the driver structure to register

59* @owner: owner module of drv

60* @mod_name: module name string

62* Adds the driver structure to the list of registered drivers.

63* Returns a negative value on error, otherwise 0.

64* If no error occurred, the driver remains registered even if

65* no device was claimed during registration.

66*/

67int __pci_register_driver(struct pci_driver *drv, struct module *owner, const char *mod_name);

68在函数中有几个初始化语句:

69drv->https://www.360docs.net/doc/9717685418.html, = drv->name;

70drv->driver.bus = &pci_bus_type;

71drv->driver.owner = owner;

72drv->driver.mod_name = mod_name;

73即是将PCI设备中的driver变量的总线指向pci_bus_type这个总线描述符,同时设置驱动的名字等。

pci_bus_type定义如下:

struct bus_type pci_bus_type = {

https://www.360docs.net/doc/9717685418.html, = "pci",

75.match = pci_bus_match,

76.uevent = pci_uevent,

77.probe = pci_device_probe,

78.remove = pci_device_remove,

79.suspend = pci_device_suspend,

80.suspend_late = pci_device_suspend_late,

81.resume_early = pci_device_resume_early,

82.resume = pci_device_resume,

83.shutdown = pci_device_shutdown,

84.dev_attrs = pci_dev_attrs,

85};

86然后再调用函数driver_register(&drv->driver);通过这个函数将这个PCI驱动中的struct device_driver driver成员变量注册到系统中去。

pci_register_driver()->__pci_register_driver()->driver_register()

driver_register()代码如下:

/**

87* driver_register - register driver with bus

88* @drv: driver to register

89*

90* We pass off most of the work to the bus_add_driver() call,

91* since most of the things we have to do deal with the bus

92* structures.

93*

94* The one interesting aspect is that we setup @drv->unloaded

95* as a completion that gets complete when the driver reference

96* count reaches 0.

98int driver_register(struct device_driver * drv)

99{

100if ((drv->bus->probe && drv->probe) ||

101(drv->bus->remove && drv->remove) ||

102(drv->bus->shutdown && drv->shutdown)) {

103printk(KERN_W ARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);

104}

105klist_init(&drv->klist_devices, NULL, NULL);

106init_completion(&drv->unloaded);

107return bus_add_driver(drv);

108}

109klist_init()是为设备驱动的klist_devices成员进行初始化,这个klist_devices 是一个对链表进行操作的包裹结构,它会链接这个驱动能够支持的那些设备。

最后就调用bus_add_driver()函数。这个函数的功能就是将这个驱动加到其所在的总线的驱动链上。

pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_at tach()

在bus_add_driver()函数中,最重要的是调用driver_attach()函数,其定义如下:/**

110* driver_attach - try to bind driver to devices.

111* @drv: driver.

112*

113* Walk the list of devices that the bus has on it and try to

114* match the driver with each one. If driver_probe_device()

115* returns 0 and the @dev->driver is set, we've found a

116* compatible pair.

117*/

118int driver_attach(struct device_driver * drv)

119{

120return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

121}

122该函数遍历这个驱动所在的总线上的所有设备,然后将这些设备与当前驱动进行匹配,以检测这个驱动是否能够支持某个设备,也即是将设备与驱动联系起来。

bus_for_each_dev函数是扫描在drv->bus这个总线上的所有设备,然后将每个设备以及当前驱动这两个指针传递给__driver_attach函数。

pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_at tach()->__driver_attach()

__driver_attach()函数是将驱动与设备联系起来的函数。

static int __driver_attach(struct device * dev, void * data)

123{

124struct device_driver * drv = data;

125

126/*

127* Lock device and try to bind to it. We drop the error

128* here and always return 0, because we need to keep trying

129* to bind to devices and some drivers will return an error

130* simply if it didn't support the device.

131*

132* driver_probe_device() will spit a warning if there

133* is an error.

134*/

135

136if (dev->parent) /* Needed for USB */

137down(&dev->parent->sem);

138down(&dev->sem);

139if (!dev->driver)

140driver_probe_device(drv, dev);

141up(&dev->sem);

142if (dev->parent)

143up(&dev->parent->sem);

144

145return 0;

146}

147在函数中有两条语句:

if (!dev->driver)

148driver_probe_device(drv, dev);

149也即是判断当前设备是否已经注册了一个驱动,如果没有注册驱动,则调用driver_probe_device()函数。

pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_at tach()->__driver_attach()->driver_probe_device()

如下:

/**

150* driver_probe_device - attempt to bind device & driver together

151* @drv: driver to bind a device to

152* @dev: device to try to bind to the driver

153*

154* First, we call the bus's match function, if one present, which should

155* compare the device IDs the driver supports with the device IDs of the

156* device. Note we don't do this ourselves because we don't know the

157* format of the ID structures, nor what is to be considered a match and

158* what is not.

159*

160* This function returns 1 if a match is found, an error if one occurs

161* (that is not -ENODEV or -ENXIO), and 0 otherwise.

162*

163* This function must be called with @dev->sem held. When called for a

164* USB interface, @dev->parent->sem must be held as well.

165*/

166int driver_probe_device(struct device_driver * drv, struct device * dev)

167{

168struct stupid_thread_structure *data;

169struct task_struct *probe_task;

170int ret = 0;

171

172if (!device_is_registered(dev))

173return -ENODEV;

174if (drv->bus->match && !drv->bus->match(dev, drv))

175goto done;

176

177pr_debug("%s: Matched Device %s with Driver %s\n",

178drv->bus->name, dev->bus_id, drv->name);

179

180data = kmalloc(sizeof(*data), GFP_KERNEL);

181if (!data)

182return -ENOMEM;

183data->drv = drv;

184data->dev = dev;

185

186if (drv->multithread_probe) {

187probe_task = kthread_run(really_probe, data,

188"probe-%s", dev->bus_id);

189if (IS_ERR(probe_task))

190ret = really_probe(data);

191} else

192ret = really_probe(data);

193

194done:

195return ret;

196}

197该函数首先会调用总线上的match函数,以判断当前的PCI驱动能否支持该PCI设备,如果可以,则继续往后面执行。

drv->bus->match函数也即是pci_bus_type中的match成员变量,它为pci_bus_match函数。

pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_at tach()->__driver_attach()->driver_probe_device()->pci_bus_match()

/**

198* pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure 199* @dev: the PCI device structure to match against

200* @drv: the device driver to search for matching PCI device id structures

201*

202* Used by a driver to check whether a PCI device present in the

203* system is in its list of supported devices. Returns the matching

204* pci_device_id structure or %NULL if there is no match.

205*/

206static int pci_bus_match(struct device *dev, struct device_driver *drv)

207{

208struct pci_dev *pci_dev = to_pci_dev(dev);

209struct pci_driver *pci_drv = to_pci_driver(drv);

210const struct pci_device_id *found_id;

211

212found_id = pci_match_device(pci_drv, pci_dev);

213if (found_id)

214return 1;

215

216return 0;

217}

218pci_bus_match函数的作用就是将PCI设备与PCI驱动进行比较以检查该驱动是否能够支持这个设备。在函数的最前面是两个宏to_pci_dev和to_pci_driver。因为在函数执行的过程中,虽然最开始传进来的是pci_driver结构与pci_dev结构,但是在执行的时候却取了这两个结构体中的device_driver和device成员变量,所以现在就要通过这两个成员变量找到之前对应的pci_driver和pci_dev结构的地址。

#define to_pci_dev(n) container_of(n, struct pci_dev, dev)

#define to_pci_driver(drv) container_of(drv,struct pci_driver, driver)

这两个宏在 3rd书上有相应的讲解,这里也就是找到E100的pci_driver:e100_driver以及该网卡设备的pci_dev结构。现在就要对它们进行比较以看它们之间是否能够联系起来。这是通过函数pci_match_device实现的。

pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_at tach()->__driver_attach()->driver_probe_device()->pci_bus_match()->pci_match_device()

/**

219* pci_match_device - Tell if a PCI device structure has a matching PCI device id structure 220* @drv: the PCI driver to match against

221* @dev: the PCI device structure to match against

222*

223* Used by a driver to check whether a PCI device present in the

224* system is in its list of supported devices. Returns the matching

225* pci_device_id structure or %NULL if there is no match.

226*/

227const struct pci_device_id *pci_match_device(struct pci_driver *drv,

228struct pci_dev *dev)

229{

230struct pci_dynid *dynid;

231

232/* Look at the dynamic ids first, before the static ones */

233spin_lock(&drv->dynids.lock);

234list_for_each_entry(dynid, &drv->dynids.list, node) {

235if (pci_match_one_device(&dynid->id, dev)) {

236spin_unlock(&drv->dynids.lock);

237return &dynid->id;

238}

239}

240spin_unlock(&drv->dynids.lock);

241

242return pci_match_id(drv->id_table, dev);

243}

244pci_match_one_driver函数的作用是将一个PCI设备与PCI驱动进行比较,以查看它们是否相匹配。如果相匹配,则返回匹配的pci_device_id结构体指针。

此时,如果该PCI驱动已经找到了一个可以想符的PCI设备,则返回,然后再退回到之前的driver_probe_device函数中。在该函数最后将调用really_probe函数。将device_driver与device结构体指针作为参数传递到这个函数中。下面几行是调用驱动或者总线的probe函数来扫描设备。

pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_at tach()->__driver_attach()->driver_probe_device()->really_probe()

在函数really_probe()中:

if (dev->bus->probe) {

245ret = dev->bus->probe(dev);

246if (ret)

247goto probe_failed;

248} else if (drv->probe) {

249ret = drv->probe(dev);

250if (ret)

251goto probe_failed;

252}

253此时的dev->bus为pci_bus_type,其probe函数则对应为:pci_device_probe。

pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_at tach()->__driver_attach()->driver_probe_device()->really_probe()->pci_device_probe() 同样,在该函数中会获得当前的PCI设备的pci_dev结构体指针以及PCI驱动程序的pci_driver结构体指针。分别使用宏to_pci_dev和to_pci_driver。最后则调用函数__pci_device_probe。在该函数中还会调用函数pci_call_probe,这是最后的函数

pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_at tach()->__driver_attach()->driver_probe_device()->really_probe()->pci_device_probe()->__pci

_device_probe()->pci_call_probe()

在函数pci_call_probe里有一条语句:

static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,

254const struct pci_device_id *id)

255{

256int error;

257/* 省略*/

258error = drv->probe(dev, id);

259在此处就调用了pci_driver的probe函数,对于这里的E100驱动来说,它的probe函数是最开始注册的e100_probe函数,在该函数中会完成对网卡设备net_device的初始化等操作。

pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_at tach()->__driver_attach()->driver_probe_device()->really_probe()->pci_device_probe()->__pci _device_probe()->pci_call_probe()->e100_probe()

到这里,我们对网卡驱动的PCI层的初始化分析就告一个段落了,剩下的部分就是网卡驱动对网卡设备本身的初始化等操作。

Linux设备驱动程序举例

Linux设备驱动程序设计实例2007-03-03 23:09 Linux系统中,设备驱动程序是操作系统内核的重要组成部分,在与硬件设备之间 建立了标准的抽象接口。通过这个接口,用户可以像处理普通文件一样,对硬件设 备进行打开(open)、关闭(close)、读写(read/write)等操作。通过分析和设计设 备驱动程序,可以深入理解Linux系统和进行系统开发。本文通过一个简单的例子 来说明设备驱动程序的设计。 1、程序清单 //MyDev.c 2000年2月7日编写 #ifndef __KERNEL__ #define __KERNEL__//按内核模块编译 #endif #ifndef MODULE #define MODULE//设备驱动程序模块编译 #endif #define DEVICE_NAME "MyDev" #define OPENSPK 1 #define CLOSESPK 2 //必要的头文件 #include //同kernel.h,最基本的内核模块头文件 #include //同module.h,最基本的内核模块头文件 #include //这里包含了进行正确性检查的宏 #include //文件系统所必需的头文件 #include //这里包含了内核空间与用户空间进行数据交换时的函数宏 #include //I/O访问 int my_major=0; //主设备号 static int Device_Open=0; static char Message[]="This is from device driver"; char *Message_Ptr; int my_open(struct inode *inode, struct file *file) {//每当应用程序用open打开设备时,此函数被调用 printk ("\ndevice_open(%p,%p)\n", inode, file); if (Device_Open) return -EBUSY;//同时只能由一个应用程序打开 Device_Open++; MOD_INC_USE_COUNT;//设备打开期间禁止卸载 return 0; } static void my_release(struct inode *inode, struct file *file)

(整理)嵌入式系统的以太网接口设计及linux内核网络设备驱动.

嵌入式系统的以太网接口设计及linux驱动 1 以太网概述 以太网(Ethernet)是当今局域网采用的最通用的通信协议标准。在以太网中,所有计算机被连接在一条电缆上,采用带冲突检测的载波侦听多路访问(CSMA/CD)方法,采用竞争机制和总线拓扑结构。基本上,以太网由共享传输媒体,如双绞线电缆或同轴电缆、多端口集线器、网桥或交换机构成。 按照OSI(Open System Interconnection Reference Model,开放式系统互联参考模型)7层参考模型,以太网定义的是物理层(PHY)和数据链路层(对应以太网的MAC层)的标准。 2 嵌入式处理器上扩展以太网接口 以太网接口控制器主要包括MAC乘PHY两部分,如图1所示为嵌入式处理器集成MAC层控制器。 MAC层控制器和PHY的连接是通过MII、RMII等接口实现的。在IEEE802的标准系列中,数据链路层包括LLC和MAC两个子层。其中MAC负责完成数据帧的封装、解封、发送和接受功能。PHY层的结构随着传输速率的不同而有一定的差异。对于1OBaseT等网络,从以太网PHY芯片输出的就是传输所需的差分信号。但是还需要一个网络隔离变压器组成图2的结构。网络隔离变压器可起到抑制共模干扰、隔离线路以及阻抗匹配等作用。 本文介绍一种新款网络接口芯片DM9000A,它可以很方便的实现与嵌入式CPU的接口,实现扩展以太网口的功能。DM9000A是中国台湾DAVICOM公司推出的一款高速以太网接口芯片,其基本特征是:集成10/100M物理层接口;内部带有16K字节SRAM用作接收发送的FIFO缓存;支持8/16bit两种主机工作模式:

设备驱动加到Linux内核中

7.2.3 设备驱动加到Linux内核中 设备驱动程序编写完后将该驱动程序加到内核中。这需要修改Linux 的源代码,然后重新编译内核。 ①将设备驱动程序文件(比如mydriver.c)复制到/Linux/drivers/char目录下。该目录保存了Linux下字符设备的设备驱动程序。修改该目录下mem.c 文件,在int chr_dev_init()函数中增加如下代码: #ifdef CONFIG_MYDRIVER device_init(); #endif 其中CONFIG_MYDRIVER是在配置Linux内核时赋值。 ②在/linux/drivers/char目录下Makefile中增加如下代码: ifeq ($(CONFIG_MYDRIVER),y) L_OBJ + = mydriver.o endif 如果在配置Linux内核时选择了支持新定义的设备,则在编译内核时会编译mydriver.c生成mydriver.o文件。 ③修改/linux/drivers/char目录下config.in文件,在 comment Character devices 语句下面加上 bool suppot for mydriver CONFIG_MYDRIVER 这样,若编译内核,运行make config,make menuconfig或make xconfig,那么在配置字符设备时就会有选项: Support for mydriver 当选中这个设备时,设备驱动就加到了内核中了。 重新编译内核,在shell中将当前目录cd 到Linux目录下,然后执行以下代码: # make menuconfig # make dep # make 在配置选项时要注意选择支持用户添加的设备。这样得到的内核就包含用户的设备驱动程序。 Linux通过设备文件来提供应用程序和设备驱动的接口,应用程序通过标准的文件操作函数来打开、关闭、读取和控制设备。查看Linux文件系统下的/proc/devices,可以看到当前的设备信息。如果设备驱动程序已被成功加进,这里应该由该设备对应的项。/proc/interrupts纪录了当时中断情况,可以用来查看中断申请是否正常;对于DMA和I/O口的使用,在/proc下都有相应的文件进行记录;还可以在设备驱动程序中申请在/proc 文件系统下创建一个文件,该文件用来存放设备相关信息。这样通过查看该文件就可以了解设备的使用情况。总之,/proc文件系统为用户提供了查

Linux网络设备驱动开发实验

实验三:Linux网络设备驱动开发实验 一、实验目的 读懂linux网络设备驱动程序例子,并且实际加载驱动程序,加载进操作系统以后,会随着上层应用程序的触发而执行相应动作,具体执行的动作可以通过代码进行改变。 ●读懂源码及makefile ●编译驱动程序 ●加载 ●多种形式触发动作 二、预备知识 熟悉linux驱动基本原理,能读懂简单的makefile。 三、实验预计时间 80-120分钟左右 四、驱动程序部分具体步骤 要求读懂一个最简单的驱动程序,在驱动程序的诸如“xxx_open”、“xxx_read”等标准接口里面加入打印语句。可参考多模式教学网上的驱动样例。 五、用于触发驱动动作的应用程序及命令 驱动程序就是以静态的标准接口库函数形式存在,网络设备驱动会受到两大类情况的触发,一种是linux里面的控制台里面的命令,另一种是套接口应用程序,首先要搞清都有哪些具体的命令和应用程序流程,应用程序参考多模式教学网的例子。 六、运行测试 提示:需要将驱动程序以dll加载进系统中,并且触发应用程序调用各种文件操作的接口函数,使得驱动有所动作,打印出相关信息。 1.编译驱动: cd /某某目录/vnetdev/ make clean make 2.加载驱动与打开网卡: insmod netdrv.ko

ifconfig vnet0 up 3.运行应用程序 ../raw 4.通过命令“修改网卡MTU”触发驱动执行动作: ifconfig vnet0 mtu 1222 5.显示内核打印: cat /var/log/messages 6.卸载: ifconfig vnet0 down rmmod netdrv.ko 7.修改代码中的某些函数中的打印信息,重新试验上述流程。 至此大家都应该真正理解和掌握了驱动程序-操作系统-应用程序的三者联动机制。 七、实验结果 由图可知能正常加载网卡驱动,并且能够打印调试信息。

Linux内核驱动加载顺序

Linux内核驱动加载顺序 【问题】 背光驱动初始化先于LCD驱动初始化,导致LCD驱动初始化时出现闪屏的现象。 【解决过程】 1 mach-xxx.c中platform devices列表如下 /* platform devices */ static struct platform_device *athena_evt_platform_devices[] __initdata = { //&xxx_led_device, &xxx_rtc_device, &xxx_uart0_device, &xxx_uart1_device, &xxx_uart2_device, &xxx_uart3_device, &xxx_nand_device, &xxx_i2c_device, &xxx_lcd_device, &xxxpwm_backlight_device, ... }; LCD(xxx_lcd_device)设备先于PWM(xxxpwm_backlight_device)设备。 可见驱动的初始化顺序并不是和这个表定义的顺序始终保持一致的。(记得PM操作 - resume/suspend 的顺序 是和这个表的顺序保持一致的) 2 怀疑和编译顺序有关 Z:\kernel\drivers\video\Makefile:背光驱动(backlight/)的编译限于LCD驱动(xxxfb.o)的编译 obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += backlight/ display/ ... obj-$(CONFIG_FB_xxx) += xxxfb.o ak_logo.o obj-$(CONFIG_FB_AK88) += ak88-fb/ 这样编译生成的System.map中的顺序为: 906 c001f540 t __initcall_pwm_backlight_init6 907 c001f544 t __initcall_display_class_init6 908 c001f548 t __initcall_xxxfb_init6 Makefile更改为: obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += display/

linux设备驱动中常用函数

Linux2.6设备驱动常用的接口函数(一) ----字符设备 刚开始,学习linux驱动,觉得linux驱动很难,有字符设备,块设备,网络设备,针对每一种设备其接口函数,驱动的架构都不一样。这么多函数,要每一个的熟悉,那可多难啦!可后来发现linux驱动有很多规律可循,驱动的基本框架都差不多,再就是一些通用的模块。 基本的架构里包括:加载,卸载,常用的读写,打开,关闭,这是那种那基本的咯。利用这些基本的功能,当然无法实现一个系统。比方说:当多个执行单元对资源进行访问时,会引发竞态;当执行单元获取不到资源时,它是阻塞还是非阻塞?当突然间来了中断,该怎么办?还有内存管理,异步通知。而linux 针对这些问题提供了一系列的接口函数和模板框架。这样,在实际驱动设计中,根据具体的要求,选择不同的模块来实现其功能需求。 觉得能熟练理解,运用这些函数,是写号linux设备驱动的第一步。因为是设备驱动,是与最底层的设备打交道,就必须要熟悉底层设备的一些特性,例如字符设备,块设备等。系统提供的接口函数,功能模块就像是工具,能够根据不同的底层设备的的一些特性,选择不同的工具,方能在linux驱动中游刃有余。 最后就是调试,这可是最头疼的事。在调试过程中,总会遇到这样,那样的问题。怎样能更快,更好的发现并解决这些问题,就是一个人的道行咯!我个人觉得: 发现问题比解决问题更难! 时好时坏的东西,最纠结! 看得见的错误比看不见的错误好解决! 一:Fops结构体中函数: ①ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以-EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型). ②ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数 ③loff_t (*llseek) (struct file *, loff_t, int); llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示. 如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述). ④int (*open) (struct inode *, struct file *);

Linux设备驱动模型之platform总线深入浅出

Linux设备驱动模型之platform总线深入浅出 在Linux2.6以后的设备驱动模型中,需关心总线,设备和驱动这三种实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。 对于依附在USB、PCI、I2C、SPI等物理总线来这些都不是问题。但是在嵌入式系统里面,在Soc系统中集成的独立外设控制器,挂接在Soc内存空间的外设等却不依附在此类总线。基于这一背景,Linux发明了一种总线,称为platform。相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。 platform总线相关代码:driver\base\platform.c 文件相关结构体定义:include\linux\platform_device.h 文件中 platform总线管理下最重要的两个结构体是platform_device和platform_driver 分别表示设备和驱动在Linux中的定义如下一:platform_driver //include\linux\platform_device.h 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 (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *);//恢复函数,在开机时被调用struct device_driver driver;//设备驱动结构}; 1 2 3 4 5 6 7 8

一个简单的演示用的Linux字符设备驱动程序.

实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用cat命令或者自编的readtest命令读出"设备"里的内容 --以8139网卡为例,演示了I/O端口和I/O内存的使用 本文中的大部分内容在Linux Device Driver这本书中都可以找到, 这本书是Linux驱动开发者的唯一圣经。 ================================================== ===== 先来看看整个驱动程序的入口,是char8139_init(这个函数 如果不指定MODULE_LICENSE("GPL", 在模块插入内核的 时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的 "GPL"属性。 module_init(char8139_init; module_exit(char8139_exit; MODULE_LICENSE("GPL"; MODULE_AUTHOR("ypixunil"; MODULE_DESCRIPTION("Wierd char device driver for Realtek 8139 NIC"; 接着往下看char8139_init( static int __init char8139_init(void {

int result; PDBG("hello. init.\n"; /* register our char device */ result=register_chrdev(char8139_major, "char8139", &char8139_fops; if(result<0 { PDBG("Cannot allocate major device number!\n"; return result; } /* register_chrdev( will assign a major device number and return if it called * with "major" parameter set to 0 */ if(char8139_major == 0 char8139_major=result; /* allocate some kernel memory we need */ buffer=(unsigned char*(kmalloc(CHAR8139_BUFFER_SIZE, GFP_KERNEL; if(!buffer { PDBG("Cannot allocate memory!\n"; result= -ENOMEM;

史上最全linux内核配置详解

对于每一个配置选项,用户可以回答"y"、"m"或"n"。其中"y"表示将相应特性的支持或设备驱动程序编译进内核;"m"表示将相应特性的支持或设备驱动程序编译成可加载模块,在需要时,可由系统或用户自行加入到内核中去;"n"表示内核不提供相应特性或驱动程序的支持。只有<>才能选择M 1. General setup(通用选项) [*]Prompt for development and/or incomplete code/drivers,设置界面中显示还在开发或者还没有完成的代码与驱动,最好选上,许多设备都需要它才能配置。 [ ]Cross-compiler tool prefix,交叉编译工具前缀,如果你要使用交叉编译工具的话输入相关前缀。默认不使用。嵌入式linux更不需要。 [ ]Local version - append to kernel release,自定义版本,也就是uname -r可以看到的版本,可以自行修改,没多大意义。 [ ]Automatically append version information to the version string,自动生成版本信息。这个选项会自动探测你的内核并且生成相应的版本,使之不会和原先的重复。这需要Perl的支持。由于在编译的命令make-kpkg 中我们会加入- –append-to-version 选项来生成自定义版本,所以这里选N。 Kernel compression mode (LZMA),选择压缩方式。 [ ]Support for paging of anonymous memory (swap),交换分区支持,也就是虚拟内存支持,嵌入式不需要。 [*]System V IPC,为进程提供通信机制,这将使系统中各进程间有交换信息与保持同步的能力。有些程序只有在选Y的情况下才能运行,所以不用考虑,这里一定要选。 [*]POSIX Message Queues,这是POSIX的消息队列,它同样是一种IPC(进程间通讯)。建议你最好将它选上。 [*]BSD Process Accounting,允许进程访问内核,将账户信息写入文件中,主要包括进程的创建时间/创建者/内存占用等信息。可以选上,无所谓。 [*]BSD Process Accounting version 3 file format,选用的话统计信息将会以新的格式(V3)写入,注意这个格式和以前的v0/v1/v2 格式不兼容,选不选无所谓。 [ ]Export task/process statistics through netlink (EXPERIMENTAL),通过通用的网络输出工作/进程的相应数据,和BSD不同的是,这些数据在进程运行的时候就可以通过相关命令访问。和BSD类似,数据将在进程结束时送入用户空间。如果不清楚,选N(实验阶段功能,下同)。 [ ]Auditing support,审计功能,某些内核模块需要它(SELINUX),如果不知道,不用选。 [ ]RCU Subsystem,一个高性能的锁机制RCU 子系统,不懂不了解,按默认就行。 [ ]Kernel .config support,将.config配置信息保存在内核中,选上它及它的子项使得其它用户能从/proc/ config.gz中得到内核的配置,选上,重新配置内核时可以利用已有配置Enable access to .config through /proc/config.gz,上一项的子项,可以通过/proc/ config.gz访问.config配置,上一个选的话,建议选上。 (16)Kernel log buffer size (16 => 64KB, 17 => 128KB) ,内核日志缓存的大小,使用默认值即可。12 => 4 KB,13 => 8 KB,14 => 16 KB单处理器,15 => 32 KB多处理器,16 => 64 KB,17 => 128 KB。 [ ]Control Group support(有子项),使用默认即可,不清楚可以不选。 Example debug cgroup subsystem,cgroup子系统调试例子 Namespace cgroup subsystem,cgroup子系统命名空间 Device controller for cgroups,cgroups设备控制器

《Linux设备驱动开发详解:基于最新的Linux 4.0内核》19. Linux电源管理系统架构和驱动

以下电子书来源于宋宝华《Linux设备驱动开发详解:基于最新的Linux 4.0内核》第19章《Linux电源管理系统架构和驱动》 本章导读 Linux在消费电子领域的应用已经铺天盖地,而对于消费电子产品而言,省电是一个重要的议题。 本章将介绍Linux设备树(Device Tree)的起源、结构和因为设备树而引起的驱动和BSP 变更。 19.1节阐述了Linux电源管理的总体架构。 19.2~19.8节分别论述了CPUFreq、CPUIdle、CPU热插拔以及底层的基础设施Regulator、OPP以及电源管理的调试工具PowerTop。 19.9节讲解了系统Suspend to RAM的过程以及设备驱动如何提供对Suspend to RAM的支持。 19.10节讲解了设备驱动的Runtime suspend。 本章是相对《Linux设备驱动开发详解(第2版)》全新的一章内容,也是Linux设备驱动工程师必备的知识体系。

第十九章Linux电源管理系统架构和驱动 1.Linux电源管理全局架构 Linux电源管理非常复杂,牵扯到系统级的待机、频率电压变换、系统空闲时的处理以及每个设备驱动对于系统待机的支持和每个设备的运行时电源管理,可以说和系统中的每个设备驱动都息息相关。 对于消费电子产品来说,电源管理相当重要。因此,这部分工作往往在开发周期中占据相当大的比重,图19.1呈现了Linux内核电源管理的整体架构。大体可以归纳为如下几类: 1.CPU在运行时根据系统负载进行动态电压和频率变换的CPUFreq 2.CPU在系统空闲时根据空闲的情况进行低功耗模式的CPUIdle 3.多核系统下CPU的热插拔支持 4.系统和设备对于延迟的特别需求而提出申请的PM QoS,它会作用于CPUIdle的具体 策略 5.设备驱动针对系统Suspend to RAM/Disk的一系列入口函数 6.SoC进入suspend状态、SDRAM自刷新的入口 7.设备的runtime(运行时)动态电源管理,根据使用情况动态开关设备 8.底层的时钟、稳压器、频率/电压表(OPP模块完成)支撑,各驱动子系统都可能用 到 图19.1 Linux电源管理系统架构 2.CPUFreq驱动 CPUFreq子系统位于drivers/cpufreq目录,负责进行运行过程中CPU频率和电压的动态

Linux内核驱动模块编写概览-ioctl,class_create,device_create

如果你对内核驱动模块一无所知,请先学习内核驱动模块的基础知识。 如果你已经入门了内核驱动模块,但是仍感觉有些模糊,不能从整体来了解一个内核驱动模块的结构,请赏读一下这篇拙文。 如果你已经从事内核模块编程N年,并且道行高深,也请不吝赐教一下文中的疏漏错误。 本文中我将实现一个简单的Linux字符设备,旨在大致勾勒出linux内核模块的编写方法的轮廓。其中重点介绍ioctl的用途。 我把这个简单的Linux字符设备模块命名为hello_mod. 设备类型名为hello_cl ass 设备名为hello 该设备是一个虚拟设备,模块加载时会在/sys/class/中创建名为hello_class 的逻辑设备,在/dev/中创建hello的物理设备文件。模块名为hello_mod,可接受输入字符串数据(长度小于128),处理该输入字符串之后可向外输出字符串。并且可以接受ioctl()函数控制内部处理字符串的方式。 例如: a.通过write函数写入“Tom”,通过ioctl函数设置langtype=chinese,通过read函数读出的数据将会是“你好!Tom/n” b.通过write函数写入“Tom”,通过ioctl函数设置langtype=english,通过read函数读出的数据将会是“hello!Tom/n” c.通过write函数写入“Tom”,通过ioctl函数设置langtype=pinyin,通过read函数读出的数据将会是“ni hao!Tom/n” 一般的内核模块中不会负责设备类别和节点的创建,我们在编译完之后会得到.o或者.k o文件,然后insmod之后需要mk nod来创建相应文件,这个简单的例子 中我们让驱动模块加载时负责自动创建设备类别和设备文件。这个功能有两个步骤, 1)创建设备类别文件class_cr eate(); 2)创建设备文件dev ice_create(); 关于这两个函数的使用方法请参阅其他资料。 linux设备驱动的编写相对wi ndows编程来说更容易理解一点因为不需要处理IR P,应用层函数和内核函数的关联方式浅显易懂。 比如当应曾函数对我的设备调用了open()函数,而最终这个应用层函数会调用我的设备中的自定义open()函数,这个函数要怎么写呢, 我在我的设备中定义的函数名是hello_mod_open,注意函数名是可以随意定义,但是函数签名是要符合内核要求的,具体的定义是怎么样请看 static int hello_mod_open(struct inode *, struct file *); 这样就定义了内核中的open函数,这只是定义还需要与我们自己的模块关联起来,这就要用到一个结构 struct file_operations 这个结构里面的成员是对应于设备操作的各种函数的指针。 我在设备中用到了这些函数所以就如下定义,注意下面的写法不是标准ANSI C的语法,而是GNU扩展语法。 struct file_operations hello_mod_fops = { .owner = THIS_MODULE, .open = hello_mod_open,

从零开始搭建Linux驱动开发环境

参考: 韦东山视频第10课第一节内核启动流程分析之编译体验 第11课第三节构建根文件系统之busybox 第11课第四节构建根文件系统之构建根文件系统韦东山书籍《嵌入式linux应用开发完全手册》 其他《linux设备驱动程序》第三版 平台: JZ2440、mini2440或TQ2440 交叉网线和miniUSB PC机(windows系统和Vmware下的ubuntu12.04) 一、交叉编译环境的选型 具体的安装交叉编译工具,网上很多资料都有,我的那篇《arm-linux- gcc交叉环境相关知识》也有介绍,这里我只是想提示大家:构建跟文件系统中所用到的lib库一定要是本系统Ubuntu中的交叉编译环境arm-linux- gcc中的。即如果电脑ubuntu中的交叉编译环境为arm-linux-

二、主机、开发板和虚拟机要三者互通 w IP v2.0》一文中有详细的操作步骤,不再赘述。 linux 2.6.22.6_jz2440.patch组合而来,具体操作: 1. 解压缩内核和其补丁包 tar xjvf linux-2.6.22.6.tar.bz2 # 解压内核 tar xjvf linux-2.6.22.6_jz2440.tar.bz2 # 解压补丁

cd linux_2.6.22.6 patch –p1 < ../linux-2.6.22.6_jz2440.patch 3. 配置 在内核目录下执行make 2410_defconfig生成配置菜单,至于怎么配置,《嵌入式linux应用开发完全手册》有详细介绍。 4. 生成uImage make uImage 四、移植busybox 在我们的根文件系统中的/bin和/sbin目录下有各种命令的应用程序,而这些程序在嵌入式系统中都是通过busybox来构建的,每一个命令实际上都是一个指向bu sybox的链接,busybox通过传入的参数来决定进行何种命令操作。 1)配置busybox 解压busybox-1.7.0,然后进入该目录,使用make menuconfig进行配置。这里我们这配置两项 一是在编译选项选择动态库编译,当然你也可以选择静态,不过那样构建的根文件系统会比动态编译的的大。 ->Busybox Settings ->Build Options

Linux网络设备驱动

嵌入式培训专家
Linux网络设备驱动
主讲:宋宝华
https://www.360docs.net/doc/9717685418.html,

华清远见
今天的内容
vLinux网络设备驱动架构 vLinux网络设备驱动数据流程
? NON-NAPI模式数据接收流程 ? NAPI模式数据接收流程 ? 数据发送流程
vLinux网络协议栈的实现
? TCP/UDP/IP/MAC各层数据传递 ? 网络系统调用与socket

华清远见
Linux网络设备驱动架构

华清远见
net_device
struct net_device_ops { int (*ndo_open)(struct net_device *dev); int (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev); int (*ndo_set_mac_address)(struct net_device *dev, void *addr); int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); void (*ndo_tx_timeout) (struct net_device *dev); ... }
struct net_device { struct net_device_stats stats; const struct net_device_ops *netdev_ops; const struct ethtool_ops *ethtool_ops; ... }
struct ethtool_ops { int (*get_settings)(struct net_device *, struct ethtool_cmd *); int (*set_settings)(struct net_device *, struct ethtool_cmd *); void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *); int (*get_regs_len)(struct net_device *); ... }

Linux驱动框架及驱动加载

本讲主要概述Linux设备驱动框架、驱动程序的配置文件及常用的加载驱动程序的方法;并且介绍Red Hat Linux安装程序是如何加载驱动的,通过了解这个过程,我们可以自己将驱动程序放到引导盘中;安装完系统后,使用kudzu自动配置硬件程序。 Linux设备驱动概述 1. 内核和驱动模块 操作系统是通过各种驱动程序来驾驭硬件设备,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。正如我们查看屏幕上的文档时,不用去管到底使用nVIDIA芯片,还是ATI芯片的显示卡,只需知道输入命令后,需要的文字就显示在屏幕上。硬件驱动程序是操作系统最基本的组成部分,在Linux内核源程序中也占有较高的比例。 Linux内核中采用可加载的模块化设计(LKMs ,Loadable Kernel Modules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其它的代码可以选择是在内核中,或者编译为内核的模块文件。 如果需要某种功能,比如需要访问一个NTFS分区,就加载相应的NTFS模块。这种设计可以使内核文件不至于太大,但是又可以支持很多的功能,必要时动态地加载。这是一种跟微内核设计不太一样,但却是切实可行的内核设计方案。 我们常见的驱动程序就是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,而Linux最基础的驱动,如CPU、PCI总线、TCP/IP协议、APM(高级电源管理)、VFS等驱动程序则编译在内核文件中。有时也把内核模块就叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如ext3文件系统的驱动。 理解这一点很重要。因此,加载驱动时就是加载内核模块。下面来看一下有关模块的命令,在加载驱动程序要用到它们:lsmod、modprob、insmod、rmmod、modinfo。 lsmod

Linux设备驱动程序简介

第一章Linux设备驱动程序简介 Linux Kernel 系统架构图 一、驱动程序的特点 ?是应用和硬件设备之间的一个软件层。 ?这个软件层一般在内核中实现 ?设备驱动程序的作用在于提供机制,而不是提供策略,编写访问硬件的内核代码时不要给用户强加任何策略 o机制:驱动程序能实现什么功能。 o策略:用户如何使用这些功能。 二、设备驱动分类和内核模块 ?设备驱动类型。Linux 系统将设备驱动分成三种类型 o字符设备 o块设备 o网络设备 ?内核模块:内核模块是内核提供的一种可以动态加载功能单元来扩展内核功能的机制,类似于软件中的插件机制。这种功能单元叫内核模块。 ?通常为每个驱动创建一个不同的模块,而不在一个模块中实现多个设备驱动,从而实现良好的伸缩性和扩展性。 三、字符设备 ?字符设备是个能够象字节流<比如文件)一样访问的设备,由字符设备驱动程序来实现这种特性。通过/dev下的字符设备文件来访问。字符设备驱动程序通常至少需要实现 open、close、read 和 write 等系统调用 所对应的对该硬件进行操作的功能函数。 ?应用程序调用system call<系统调用),例如:read、write,将会导致操作系统执行上层功能组件的代码,这些代码会处理内核的一些内部 事务,为操作硬件做好准备,然后就会调用驱动程序中实现的对硬件进 行物理操作的函数,从而完成对硬件的驱动,然后返回操作系统上层功 能组件的代码,做好内核内部的善后事务,最后返回应用程序。 ?由于应用程序必须使用/dev目录下的设备文件<参见open调用的第1个参数),所以该设备文件必须事先创建。谁创建设备文件呢? ?大多数字符设备是个只能顺序访问的数据通道,不能前后移动访问指针,这点和文件不同。比如串口驱动,只能顺序的读写设备。然而,也 存在和数据区或者文件特性类似的字符设备,访问它们时可前后移动访

linux驱动基础试题

L i n u x驱动基础试题(时间:1个小时) 一、选择题(每题4分,共40分,包括单选和多选,多选、少选均不得分) 1.Linux系统中将设备进行分类管理,下列设备中(ACD)属于字符设备,(BC)属于块设备 [A]键盘[B]硬盘[C]闪存设备[D]帧缓存设备[E]网卡 2.Linux系统中,内核以(D)区分设备 [A]设备节点名[B]设备节点号[C]设备名称[D]设备号 3.Linux系统中设备节点可以创建在(A) [A]/dev目录下[B]根目录下[C]/tmp目录下[E]以上都可以 4.Linux驱动程序运行在(A) [A]内核空间[B]用户空间[C]用户空间和内核空间 5.Linux系统中设备驱动程序是以模块形式组织的,编译驱动时可以用哪种方式编译(AB) [A]静态编译进内核[B]动态编译 6.内核中,设备的主设备号用(B)位来表示,次设备号用(D)位来表示 [A]8[B]12[C]16[D]20[E]24[F]32 7.Linux系统中哪些种类的设备有设备节点(BD) [A]定时器[B]字符设备[C]块设备[D]网络设备 8.通常情况下,kmalloc函数能分配的最大内存是(C) [A]4K[B]64K[C]128K[D]4M 9.能保证物理空间上连续的内存分配函数是(AB) [A]__get_free_pages[B]kmalloc[C]vmalloc[D]malloc 10.Linux系统中通过add_timer添加的timer是(A) [A]一次的[B]循环的[C]以上两种都可以 二、简答题(每题6分,共60分) 系统中以模块方式组织设备驱动程序,请列举在一个模块程序中必不可少的组成部分。(可以写个Helloworld模块的程序) 2.请从定义、性质、操作方式等方面对比说明字符设备和块设备。 3.请列举Linux设备驱动程序中,程序延缓执行的机制。 4.简述Linux设备驱动中使用中断的步骤。 5.简述信号量和自旋锁的异同和使用时的注意事项。 6.简述命令mknod/dev/zeroc15的做用和命令各个部分的含义,并写出创建一个块设备节点的命令。 7.简述命令insmod,rmmod,lsmod的功能。 8.驱动程序中采用动态申请设备号的,我们如何得到对应设备的设备号? 9.简述设备驱动程序和普通应用程序的异同点。 10.简述mmap机制的作用和使用mmap的好处。

linux驱动程序进入内核

ARM-uClinux下编写加载驱动程序详细过程 本文主要介绍在uClinux下,通过加载模块的方式调试IO控制蜂鸣器的驱动程序。实验过程与上篇文章所讲的过程基本相似,更多注重细节及注意事项。 本文适合学习ARM—Linux的初学者。 //================================================================== 硬件平台:MagicARM2200教学试验开发平台(LPC2290) Linux version 2.4.24,gcc version 2.95.3 电路连接:P0.7——蜂鸣器,低电平发声。 实验条件:uClinux内核已经下载到开发板上,能够正常运行;与宿主机相连的网络、串口连接正常。 //================================================================== 编写蜂鸣器的驱动程序相对来说容易实现,不需要处理中断等繁琐的过程,本文以蜂鸣器的驱动程序为例,详细说明模块化驱动程序设计的主要过程和注意事项。 一、编写驱动程序 驱动程序的编写与上文所说的编写过程基本相同,这里再详细说明一下。 //========================================== //蜂鸣器驱动程序:beep.c文件 //------------------------------------------------------------------- #include /*模块相关*/ #include /*内核相关*/ #include /*linux定义类型*/ #include /*文件系统 file_opertions 结构体定义*/ #include /*出错信息*/ /*PINSEL0 注意:低2位是UART0复用口,不要改动*/ #define PINSEL0 (*((volatile unsigned*) 0xE002C000)) /*P0口控制寄存器*/ #define IO0PIN (*((volatile unsigned*) 0xE0028000))

相关文档
最新文档