Linux_2.6.11_MTD驱动分析 简要说明

合集下载

linux_MTD结构分析

linux_MTD结构分析

Linux MTD 结构分析作者:董磊鋆Email:dongleijun4000@专有名词:y MTD:Memory Technology Device,内存技术设备,y JEDEC:Joint Electron Device Engineering Council,电子电器设备联合会y CFI:Common Flash Interface,通用Flash接口,Intel发起的一个Flash的接口标准 y OOB: out of band,某些内存技术支持out-of-band数据——例如,NAND flash每512字节的块有16个字节的extra data,用于纠错或元数据。

y ECC: error correction,某些硬件不仅允许对flash的访问,也有ecc功能,所有flash 器件都受位交换现象的困扰。

在某些情况下,一个比特位会发生反转或被报告反转了,如果此位真的反转了,就要采用ECC算法。

y erasesize: 一个erase命令可以擦除的最小块的尺寸y buswidth:MTD设备的接口总线宽度y interleave:交错数,几块芯片平行连接成一块芯片,使buswidth变大y devicetype:芯片类型,x8、x16或者x32y NAND:一种Flash技术,参看NAND和NOR的比较y NOR:一种Flash技术,参看NAND和NOR的比较y Wear out:Flash的擦除次数有限制,一般在1000,000次左右,由于过量的擦除,使得Flash无效。

体系结构MTD(memory technology device 内存技术设备)是用于访问memory 设备(ROM、flash)的Linux 的子系统。

MTD 的主要目的是为了使新的memory 设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。

MTD 的所有源代码在/drivers/mtd 子目录下。

Linux设备驱动程序原理及框架-内核模块入门篇

Linux设备驱动程序原理及框架-内核模块入门篇

Linux设备驱动程序原理及框架-内核模块入门篇内核模块介绍应用层加载模块操作过程内核如何支持可安装模块内核提供的接口及作用模块实例内核模块内核模块介绍Linux采用的是整体式的内核结构,这种结构采用的是整体式的内核结构,采用的是整体式的内核结构的内核一般不能动态的增加新的功能。

为此,的内核一般不能动态的增加新的功能。

为此,Linux提供了一种全新的机制,叫(可安装) 提供了一种全新的机制,可安装) 提供了一种全新的机制模块” )。

利用这个机制“模块”(module)。

利用这个机制,可以)。

利用这个机制,根据需要,根据需要,在不必对内核重新编译链接的条件将可安装模块动态的插入运行中的内核,下,将可安装模块动态的插入运行中的内核,成为内核的一个有机组成部分;成为内核的一个有机组成部分;或者从内核移走已经安装的模块。

正是这种机制,走已经安装的模块。

正是这种机制,使得内核的内存映像保持最小,的内存映像保持最小,但却具有很大的灵活性和可扩充性。

和可扩充性。

内核模块内核模块介绍可安装模块是可以在系统运行时动态地安装和卸载的内核软件。

严格来说,卸载的内核软件。

严格来说,这种软件的作用并不限于设备驱动,并不限于设备驱动,例如有些文件系统就是以可安装模块的形式实现的。

但是,另一方面,可安装模块的形式实现的。

但是,另一方面,它主要用来实现设备驱动程序或者与设备驱动密切相关的部分(如文件系统等)。

密切相关的部分(如文件系统等)。

课程内容内核模块介绍应用层加载模块操作过程内核如何支持可安装模块内核提供的接口及作用模块实例内核模块应用层加载模块操作过程内核引导的过程中,会识别出所有已经安装的硬件设备,内核引导的过程中,会识别出所有已经安装的硬件设备,并且创建好该系统中的硬件设备的列表树:文件系统。

且创建好该系统中的硬件设备的列表树:/sys 文件系统。

(udev 服务就是通过读取该文件系统内容来创建必要的设备文件的。

)。

linux mtdev 用法

linux mtdev 用法

linux mtdev 用法在Linux系统中,mtdev是一个用于处理多点触摸设备输入的库。

它可以解析多点触摸设备的输入数据,并将其转换为适合应用程序处理的格式。

本文将介绍Linux mtdev的用法以及如何在应用程序中使用它。

一、安装mtdev库在开始使用mtdev之前,首先需要安装mtdev库。

在大多数Linux 发行版中,可以使用包管理工具来安装mtdev。

以Ubuntu为例,可以使用以下命令安装mtdev库:```shellsudo apt-get install libmtdev-dev```二、使用mtdev库1. 包含头文件在使用mtdev库之前,需要在应用程序中包含mtdev库的头文件。

在C语言中,可以使用以下方式包含mtdev头文件:```c#include <mtdev.h>```2. 打开输入设备接下来,需要打开多点触摸设备的输入设备节点。

输入设备节点的路径通常为`/dev/input/eventX`,其中X为设备号。

可以使用以下代码来打开输入设备:```cint fd = open("/dev/input/eventX", O_RDONLY);```注意替换X为实际的设备号。

打开输入设备成功后,会返回一个文件描述符fd。

3. 初始化mtdev在打开输入设备后,需要初始化mtdev库。

可以使用以下代码来初始化mtdev:```cstruct mtdev dev;int ret = mtdev_open(&dev, fd);```4. 读取输入事件初始化mtdev后,可以开始读取输入事件。

使用以下代码来读取输入事件:struct input_event ev;while(read(fd, &ev, sizeof(struct input_event)) > 0) {/* 在这里处理输入事件 */}```在循环中读取输入事件,直到读取结束。

Linux命令行中的硬件信息查看和驱动管理

Linux命令行中的硬件信息查看和驱动管理

Linux命令行中的硬件信息查看和驱动管理在Linux命令行中,我们可以通过一些命令来查看硬件信息和管理驱动,这对于系统维护和故障排除非常重要。

本文将介绍几个常用的命令及其用法,帮助您快速获取硬件信息和管理驱动。

1. 查看硬件信息1.1 lshw命令lshw(或者lswhw)是一个用于查看硬件信息的命令,可以列出系统中所有硬件的详细信息,包括处理器、内存、硬盘、网卡等。

使用示例:```$ sudo lshw```运行以上命令后,您将看到完整的硬件信息列表,可以通过滚动查看或者使用管道和grep命令过滤感兴趣的部分。

1.2 lspci命令lspci命令用于列出系统中所有PCI设备的信息,包括显卡、网卡、声卡等。

使用示例:```$ lspci```该命令会输出PCI设备的详细信息,可以通过管道和grep进行过滤。

1.3 lsusb命令lsusb命令用于列出系统中所有USB设备的信息。

使用示例:```$ lsusb```该命令会输出USB设备的详细信息,可以通过管道和grep进行过滤。

2. 管理驱动2.1 modprobe命令modprobe命令用于加载和卸载Linux内核模块,包括驱动程序。

使用示例:```$ sudo modprobe <module_name> // 加载模块$ sudo modprobe -r <module_name> // 卸载模块```其中,`<module_name>`为要加载或卸载的模块名称。

2.2 lsmod命令lsmod命令用于列出当前已加载的内核模块。

使用示例:```$ lsmod```该命令会输出已加载模块的列表,包括模块名称、使用次数等信息。

2.3 rmmod命令rmmod命令用于卸载已加载的内核模块。

使用示例:```$ sudo rmmod <module_name>```其中,`<module_name>`为要卸载的模块名称。

Linux设备驱动之HID驱动---非常全面而且深刻

Linux设备驱动之HID驱动---非常全面而且深刻

Linux设备驱动之HID驱动---⾮常全⾯⽽且深刻本⽂系本站原创,欢迎转载!转载请注明出处:/------------------------------------------⼀:前⾔继前⾯分析过UHCI和HUB驱动之后,接下来以HID设备驱动为例来做⼀个具体的USB设备驱动分析的例⼦.HID是Human Interface Devices的缩写.翻译成中⽂即为⼈机交互设备.这⾥的⼈机交互设备是⼀个宏观上⾯的概念,任何设备,只要符合HID spec,都⼆:HID驱动⼊⼝分析USB HID设备驱动⼊⼝位于linux-2.6.25/drivers/hid/usbhid/hid-core.c中.该module的⼊⼝为hid_init().代码如下:static int __init hid_init(void){int retval;retval = usbhid_quirks_init(quirks_param);if (retval)goto usbhid_quirks_init_fail;retval = hiddev_init();if (retval)goto hiddev_init_fail;retval = usb_register(&hid_driver);if (retval)goto usb_register_fail;info(DRIVER_VERSION ":" DRIVER_DESC);return0;usb_register_fail:hiddev_exit();hiddev_init_fail:usbhid_quirks_exit();usbhid_quirks_init_fail:return retval;}⾸先来看usbhid_quirks_init()函数.quirks我们在分析UHCI和HUB的时候也接触过,表⽰需要做某种修正的设备.该函数调⽤的参数是quirks_param.定义如下:static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL };module_param_array_named(quirks, quirks_param, charp, NULL, 0444);从此可以看出, quirks_param是MAX_USBHID_BOOT_QUIRKS元素的字符串数组.并且在加载module的时候,可以动态的指定这些值.分析到这⾥.有⼈可以反应过来了,usbhid_quirks_init()是⼀种动态进⾏HID设备修正的⽅式.具体要修正哪些设备,要修正设备的那些⽅⾯,都可以由加载模块是所带参数来决定.usbhid_quirks_init()的代码如下:int usbhid_quirks_init(char **quirks_param){u16 idVendor, idProduct;u32 quirks;int n = 0, m;for (; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) {m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",&idVendor, &idProduct, &quirks);if (m != 3 ||usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {printk(KERN_WARNING"Could not parse HID quirk module param %s\n",quirks_param[n]);}}return0;}由此可以看出, quirks_param数组中的每⼀项可以分为三个部份,分别是要修正设备的VendorID,ProductID和要修正的功能.⽐如0x1000 0x0001 0x0004就表⽰:要忽略掉VendorID为0x1000,ProductID为0x0004的设备.(在代码中,有#define HID_QUIRK_跟进usbhid_modify_dquirk()函数,代码如下:int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,const u32 quirks){struct quirks_list_struct *q_new, *q;int list_edited = 0;if (!idVendor) {dbg_hid("Cannot add a quirk with idVendor = 0\n");return -EINVAL;}q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);if (!q_new) {dbg_hid("Could not allocate quirks_list_struct\n");return -ENOMEM;}q_new->hid_bl_item.idVendor = idVendor;q_new->hid_bl_item.idProduct = idProduct;q_new->hid_bl_item.quirks = quirks;down_write(&dquirks_rwsem);list_for_each_entry(q, &dquirks_list, node) {if (q->hid_bl_item.idVendor == idVendor &&q->hid_bl_item.idProduct == idProduct) {list_replace(&q->node, &q_new->node);kfree(q);list_edited = 1;break;}}if (!list_edited)list_add_tail(&q_new->node, &dquirks_list);up_write(&dquirks_rwsem);return0;}这个函数⽐较简单,就把quirks_param数组项中的三个部份存⼊⼀个封装结构.然后将其结构挂载到dquirks_list表.如果dquirks_list有重复的VendorId和ProductID就更新其quirks信息.经过usbhid_quirks_init()之后,所有要修正的设备的相关操作都会存放在dquirks_list中.返回到hid_init(),继续往下⾯分析.hiddev_init()是⼀个⽆关的操作,不会影响到后⾯的操作.忽略后⾯就是我们今天要分析的重点了,如下:retval = usb_register(&hid_driver);通过前⾯对HUB的驱动分析,相信对usb_redister()应该很熟悉了.hid_driver定义如下:static struct usb_driver hid_driver = {.name = "usbhid",.probe = hid_probe,.disconnect = hid_disconnect,.suspend = hid_suspend,.resume = hid_resume,.reset_resume = hid_post_reset,.pre_reset = hid_pre_reset,.post_reset = hid_post_reset,.id_table = hid_usb_ids,.supports_autosuspend = 1,};其中,id_table的结构为hid_usb_ids.定义如下:static struct usb_device_id hid_usb_ids [] = {{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,.bInterfaceClass = USB_INTERFACE_CLASS_HID },{ } /* Terminating entry */};也就是说,该驱动会匹配interface的ClassID,所有ClassID为USB_INTERFACE_CLASS_HID的设备都会被这个驱动所匹配.所以,所有USB HID设备都会由这个module来驱动.三:HID驱动的probe过程从上⾯的分析可看到,probe接⼝为hid_probe().定义如下:static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id){struct hid_device *hid;char path[64];int i;char *c;dbg_hid("HID probe called for ifnum %d\n",intf->altsetting->desc.bInterfaceNumber);//config the hid deviceif (!(hid = usb_hid_configure(intf)))return -ENODEV;usbhid_init_reports(hid);hid_dump_device(hid);if (hid->quirks & HID_QUIRK_RESET_LEDS)usbhid_set_leds(hid);if (!hidinput_connect(hid))hid->claimed |= HID_CLAIMED_INPUT;if (!hiddev_connect(hid))hid->claimed |= HID_CLAIMED_HIDDEV;if (!hidraw_connect(hid))hid->claimed |= HID_CLAIMED_HIDRAW;usb_set_intfdata(intf, hid);if (!hid->claimed) {printk ("HID device claimed by neither input, hiddev nor hidraw\n");hid_disconnect(intf);return -ENODEV;}if ((hid->claimed & HID_CLAIMED_INPUT))hid_ff_init(hid);if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),intf->cur_altsetting->desc.bInterfaceNumber);printk(KERN_INFO);if (hid->claimed & HID_CLAIMED_INPUT)printk("input");if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) ||hid->claimed & HID_CLAIMED_HIDRAW))printk(",");if (hid->claimed & HID_CLAIMED_HIDDEV)printk("hiddev%d", hid->minor);if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) &&(hid->claimed & HID_CLAIMED_HIDRAW))printk(",");if (hid->claimed & HID_CLAIMED_HIDRAW)printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor);c = "Device";for (i = 0; i < hid->maxcollection; i++) {if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&(hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&(hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {c = hid_types[hid->collection[i].usage & 0xffff];break;}}usb_make_path(interface_to_usbdev(intf), path, 63);printk(": USB HID v%x.%02x %s [%s] on %s\n",hid->version >> 8, hid->version & 0xff, c, hid->name, path);return0;}这个函数看起来是不是让⼈⼼慌慌?其实这个函数的最后⼀部份就是打印出⼀个Debug信息,我们根本就不需要去看. hiddev_connect()和hidraw_connect()是⼀个选择编译的操作,也不可以不要去理会.然后,剩下的就没多少了.3.1:usb_hid_configure()函数分析先来看usb_hid_configure().顾名思义,该接⼝⽤来配置hid设备.怎么配置呢?还是深⼊到代码来分析,该函数有⼀点长,分段分析如下:static struct hid_device *usb_hid_configure(struct usb_interface *intf){struct usb_host_interface *interface = intf->cur_altsetting;struct usb_device *dev = interface_to_usbdev (intf);struct hid_descriptor *hdesc;struct hid_device *hid;u32 quirks = 0;unsigned rsize = 0;char *rdesc;int n, len, insize = 0;struct usbhid_device *usbhid;quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));/* Many keyboards and mice don't like to be polled for reports,* so we will always set the HID_QUIRK_NOGET flag for them. *///如果是boot设备,跳出.不由此驱动处理if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD ||interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)quirks |= HID_QUIRK_NOGET;}//如果是要忽略的if (quirks & HID_QUIRK_IGNORE)return NULL;if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&(interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))return NULL;⾸先找到该接⼝需要修正的操作,也就是上⾯代码中的quirks值,如果没有修正操作,则quirks为0.另外,根据usb hid spec中的定义,subclass如果为1,则说明该设备是⼀个boot阶段使⽤的hid设备,然后Protocol Code为1和2时分别代表Keyboard和Mouse. 如//get hid descriptorsif (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&(!interface->desc.bNumEndpoints ||usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {dbg_hid("class descriptor not present\n");return NULL;}//bNumDescriptors:⽀持的附属描述符数⽬for (n = 0; n < hdesc->bNumDescriptors; n++)if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);//如果Report_Descriptors长度不合法if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {dbg_hid("weird size of report descriptor (%u)\n", rsize);return NULL;}if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {dbg_hid("couldn't allocate rdesc memory\n");return NULL;}//Set idle_time = 0hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);//Get Report_Descriptorsif ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {dbg_hid("reading report descriptor failed\n");kfree(rdesc);return NULL;}//是否属于fixup?usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct), rdesc,rsize, rdesc_quirks_param);dbg_hid("report descriptor (size %u, read %d) = ", rsize, n);for (n = 0; n < rsize; n++)dbg_hid_line(" %02x", (unsigned char) rdesc[n]);dbg_hid_line("\n");对于HID设备来说,在interface description之后会附加⼀个hid description, hid description中的最后部份包含有Report description或者Physical Descriptors的长度.在上⾯的代码中,⾸先取得附加在interface description之后的hid description,然后,再从hid description中取得report description的长度.最后,取得report description的详细信息.在这⾥,还会将idle时间设备为0,表⽰⽆限时,即,从上⼀次报表传输后,只有在报表发⽣改变时,才会传送此报表内容,否则,传送NAK.这段代码的最后⼀部份是相关的fixup操作,不做详细分析.//pasrse the report_descriptorif (!(hid = hid_parse_report(rdesc, n))) {dbg_hid("parsing report descriptor failed\n");kfree(rdesc);return NULL;}kfree(rdesc);hid->quirks = quirks;if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))goto fail_no_usbhid;hid->driver_data = usbhid;usbhid->hid = hid;解析获得的report description,解析之后的信息,存放在hid_device->collection和hid_device->report_enum[ ]中,这个解析过程之后会做详细分析.然后,初始化⼀个usbhid_device结构,使usbhid_device->hid指向刚解析report description获得的hid_device.同 usbhid->bufsize = HID_MIN_BUFFER_SIZE;//计算各传输⽅向的最⼤bufferhid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize);hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize);hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize);if (usbhid->bufsize > HID_MAX_BUFFER_SIZE)usbhid->bufsize = HID_MAX_BUFFER_SIZE;//in⽅向的传输最⼤值hid_find_max_report(hid, HID_INPUT_REPORT, &insize);if (insize > HID_MAX_BUFFER_SIZE)insize = HID_MAX_BUFFER_SIZE;if (hid_alloc_buffers(dev, hid)) {hid_free_buffers(dev, hid);goto fail;}计算传输数据的最⼤缓存区,并以这个⼤⼩为了hid设备的urb传输分配空间.另外,这⾥有⼀个最⼩值限制即代码中所看到的HID_MIN_BUFFER_SIZE,为64, 即⼀个⾼速设备的⼀个端点⼀次传输的数据量.在这⾥定义最⼩值为64是为了照顾低速然后,调⽤hid_alloc_buffers()为hid的urb传输初始化传输缓冲区.另外,需要注意的是,insize为INPUT⽅向的最⼤数据传输量.// 初始化usbhid->urbin和usbhid->usboutfor (n = 0; n < interface->desc.bNumEndpoints; n++) {struct usb_endpoint_descriptor *endpoint;int pipe;int interval;endpoint = &interface->endpoint[n].desc;//不是中断传输退出if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */continue;interval = endpoint->bInterval;/* Change the polling interval of mice. *///修正⿏标的双击时间if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)interval = hid_mousepoll_interval;if (usb_endpoint_dir_in(endpoint)) {if (usbhid->urbin)continue;if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL)))goto fail;pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize,hid_irq_in, hid, interval);usbhid->urbin->transfer_dma = usbhid->inbuf_dma;usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;} else {if (usbhid->urbout)continue;if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL)))goto fail;pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress);usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0,hid_irq_out, hid, interval);usbhid->urbout->transfer_dma = usbhid->outbuf_dma;usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;}}if (!usbhid->urbin) {err_hid("couldn't find an input interrupt endpoint");goto fail;}遍历接⼝中的所有endpoint,并初始化in中断传输⽅向和out中断⽅向的urb.如果⼀个hid设备没有in⽅向的中断传输,⾮法.另外,在这⾥要值得注意的是, 在为OUT⽅向urb初始化的时候,它的传输缓存区⼤⼩被设为了0.IN⽅向的中断传输缓存区⼤⼩被设为了insize,传输缓存区⼤⼩在submit的时候会修正的. init_waitqueue_head(&hid->wait);INIT_WORK(&usbhid->reset_work, hid_reset);setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);spin_lock_init(&usbhid->inlock);spin_lock_init(&usbhid->outlock);spin_lock_init(&usbhid->ctrllock);hid->version = le16_to_cpu(hdesc->bcdHID);hid->country = hdesc->bCountryCode;hid->dev = &intf->dev;usbhid->intf = intf;usbhid->ifnum = interface->desc.bInterfaceNumber;hid->name[0] = 0;if (dev->manufacturer)strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));if (dev->product) {if (dev->manufacturer)strlcat(hid->name, "", sizeof(hid->name));strlcat(hid->name, dev->product, sizeof(hid->name));}if (!strlen(hid->name))snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));hid->bus = BUS_USB;hid->vendor = le16_to_cpu(dev->descriptor.idVendor);hid->product = le16_to_cpu(dev->descriptor.idProduct);usb_make_path(dev, hid->phys, sizeof(hid->phys));strlcat(hid->phys, "/input", sizeof(hid->phys));len = strlen(hid->phys);if (len < sizeof(hid->phys) - 1)snprintf(hid->phys + len, sizeof(hid->phys) - len,"%d", intf->altsetting[0].desc.bInterfaceNumber);if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)hid->uniq[0] = 0;初始化hid的相关信息.//初始化hid 的ctrl传输usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);if (!usbhid->urbctrl)goto fail;usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,usbhid->ctrlbuf, 1, hid_ctrl, hid);usbhid->urbctrl->setup_dma = usbhid->cr_dma;usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);hid->hidinput_input_event = usb_hidinput_input_event;hid->hid_open = usbhid_open;hid->hid_close = usbhid_close;#ifdef CONFIG_USB_HIDDEVhid->hiddev_hid_event = hiddev_hid_event;hid->hiddev_report_event = hiddev_report_event;#endifhid->hid_output_raw_report = usbhid_output_raw_report;return hid;初始化usbhid的控制传输urb,之后⼜初始化了usbhid的⼏个操作函数.这个操作有什么⽤途,等⽤到的时候再来进⾏分析.fail:usb_free_urb(usbhid->urbin);usb_free_urb(usbhid->urbout);usb_free_urb(usbhid->urbctrl);hid_free_buffers(dev, hid);kfree(usbhid);fail_no_usbhid:hid_free_device(hid);return NULL;}经过上⾯的分析之后,我们对这个函数的⼤概操作有了⼀定的了解.现在分析⾥⾯调⽤的⼀些重要的⼦调函数.等这些⼦函数全部分析完了之后,不妨回过头看下这个函数.3.1.1:hid_parse_report()分析第⼀个要分析的函数是hid_parse_report().该函数⽤来解析report description.解析report description是⼀个繁杂的过程,对这个描述符不太清楚的,仔细看⼀下spec.在这⾥我们只会做代码上的分析.代码如下:struct hid_device *hid_parse_report(__u8 *start, unsigned size){struct hid_device *device;struct hid_parser *parser;struct hid_item item;__u8 *end;unsigned i;static int (*dispatch_type[])(struct hid_parser *parser,struct hid_item *item) = {hid_parser_main,hid_parser_global,hid_parser_local,hid_parser_reserved};if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL)))return NULL;//默认HID_DEFAULT_NUM_COLLECTIONS 项if (!(device->collection = kzalloc(sizeof(struct hid_collection) *HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) {kfree(device);return NULL;}//hid_device->collection_size: collection的项数device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;for (i = 0; i < HID_REPORT_TYPES; i++)INIT_LIST_HEAD(&device->report_enum[i].report_list);if (!(device->rdesc = kmalloc(size, GFP_KERNEL))) {kfree(device->collection);kfree(device);return NULL;}//hid_device->rdesc存放report_descriptor,hid_device->size存放这个描述符的⼤⼩memcpy(device->rdesc, start, size);device->rsize = size;if (!(parser = vmalloc(sizeof(struct hid_parser)))) {kfree(device->rdesc);kfree(device->collection);kfree(device);return NULL;}memset(parser, 0, sizeof(struct hid_parser));parser->device = device;end = start + size;while ((start = fetch_item(start, end, &item)) != NULL) {//long item在这⾥暂不做parseif (item.format != HID_ITEM_FORMAT_SHORT) {dbg_hid("unexpected long global item\n");hid_free_device(device);vfree(parser);return NULL;}//parse the short itemif (dispatch_type[item.type](parser, &item)) {dbg_hid("item %u %u %u %u parsing failed\n",item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);hid_free_device(device);vfree(parser);return NULL;}//如果全部解析完了if (start == end) {if (parser->collection_stack_ptr) {dbg_hid("unbalanced collection at end of report description\n");hid_free_device(device);vfree(parser);return NULL;}if (parser->local.delimiter_depth) {dbg_hid("unbalanced delimiter at end of report description\n");hid_free_device(device);vfree(parser);return NULL;}vfree(parser);return device;}}dbg_hid("item fetching failed at offset %d\n", (int)(end - start));hid_free_device(device);vfree(parser);return NULL;}进⼊到这个函数,我们⾸先看到的是Main,Globa,Local标签的解析函数.然后,分配并初始化了hid_device结构和hid_ parser.在代码中我们看到,hid_ parser-> device指向了hid_device.后hid_device没有任何域指向hid_parser. 实际上hid_parser只是⼀个辅另外,hid_device-> rdesc保存了⼀份report description副本.然后,就开始对report description的解析.函数fetch_item()⽤来取出report description的⼀项数据.代码如下:static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item){u8 b;//合法性检测if ((end - start) <= 0)return NULL;//取前⾯⼀个字节.对于短项.它的⾸个字节定义了bsize,bType,bTag.⽽对于长项,它的值为0xFEb = *start++;item->type = (b >> 2) & 3;item->tag = (b >> 4) & 15;//如果为长项.它的Type和Tag在其后的⼆个字节中.item->data.longdata指向数据的起始位置if (item->tag == HID_ITEM_TAG_LONG) {item->format = HID_ITEM_FORMAT_LONG;if ((end - start) < 2)return NULL;item->size = *start++;item->tag = *start++;if ((end - start) < item->size)return NULL;item->data.longdata = start;start += item->size;return start;}//对于短项的情况.取得size值.并根据size值取得它的data域item->format = HID_ITEM_FORMAT_SHORT;item->size = b & 3;switch (item->size) {case0:return start;case1:if ((end - start) < 1)return NULL;item->data.u8 = *start++;return start;case2:if ((end - start) < 2)return NULL;item->data.u16 = le16_to_cpu(get_unaligned((__le16*)start));start = (__u8 *)((__le16 *)start + 1);return start;case3:item->size++;if ((end - start) < 4)return NULL;item->data.u32 = le32_to_cpu(get_unaligned((__le32*)start));start = (__u8 *)((__le32 *)start + 1);return start;}return NULL;}对照代码中的注释,应该很容易看懂这个函数,不再详细分析.返回到hid_parse_report()中,取得相应项之后,如果是长项,这⾥不会做处理.对于短项.为不同的type调⽤不同的解析函数.3.1.1.1:Global项解析Global的解析⼊⼝是hid_parser_global().代码如下:static int hid_parser_global(struct hid_parser *parser, struct hid_item *item){switch (item->tag) {//PUSH项case HID_GLOBAL_ITEM_TAG_PUSH:if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {dbg_hid("global enviroment stack overflow\n");return -1;}memcpy(parser->global_stack + parser->global_stack_ptr++,&parser->global, sizeof(struct hid_global));return0;//POP项case HID_GLOBAL_ITEM_TAG_POP:if (!parser->global_stack_ptr) {dbg_hid("global enviroment stack underflow\n");return -1;}memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr,sizeof(struct hid_global));return0;case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:parser->age_page = item_udata(item);return0;case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:parser->global.logical_minimum = item_sdata(item);return0;case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:if (parser->global.logical_minimum < 0)parser->global.logical_maximum = item_sdata(item);elseparser->global.logical_maximum = item_udata(item);return0;case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:parser->global.physical_minimum = item_sdata(item);return0;case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:if (parser->global.physical_minimum < 0)parser->global.physical_maximum = item_sdata(item);elseparser->global.physical_maximum = item_udata(item);return0;case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:parser->global.unit_exponent = item_sdata(item);return0;case HID_GLOBAL_ITEM_TAG_UNIT:parser->global.unit = item_udata(item);return0;case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:if ((parser->global.report_size = item_udata(item)) > 32) {dbg_hid("invalid report_size %d\n", parser->global.report_size);return -1;}return0;case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {dbg_hid("invalid report_count %d\n", parser->global.report_count);return -1;}return0;case HID_GLOBAL_ITEM_TAG_REPORT_ID:if ((parser->global.report_id = item_udata(item)) == 0) {dbg_hid("report_id 0 is invalid\n");return -1;}return0;default:dbg_hid("unknown global tag 0x%x\n", item->tag);return -1;}}这个函数虽然长,但是逻辑很简单,对于global信息,存放在hid_parse->global中.如果遇到了PUSH项,将当前的global项⼊栈,栈即为hid_parse-> global_stack[ ].当前的栈顶位置由hid_parse-> global_stack_ptr指定.如果遇到了POP项,就将栈中的global信息出栈.3.1.1.2:Local项解析Local项解析的相应接⼝为hid_parser_local().代码如下:static int hid_parser_local(struct hid_parser *parser, struct hid_item *item){__u32 data;unsigned n;if (item->size == 0) {dbg_hid("item data expected for local item\n");return -1;}data = item_udata(item);switch (item->tag) {//DELIMITER项,定义⼀个Local项的开始case HID_LOCAL_ITEM_TAG_DELIMITER://data>1:⼀个local项开始,0:⼀个local项结束//parse->local.delimiter_branch:表⽰local项计数.//进⼊⼀个local项时,local.delimiter_depth为1,退出⼀个local项时local.delimiter_depth为0// TODO: Local项不能嵌套if (data) {/** We treat items before the first delimiter* as global to all usage sets (branch 0).* In the moment we process only these global* items and the first delimiter set.*/if (parser->local.delimiter_depth != 0) {dbg_hid("nested delimiters\n");return -1;}parser->local.delimiter_depth++;parser->local.delimiter_branch++;} else {if (parser->local.delimiter_depth < 1) {dbg_hid("bogus close delimiter\n");return -1;}parser->local.delimiter_depth--;}return1;//以下各项不能出现在有DELIMITER标签的地⽅case HID_LOCAL_ITEM_TAG_USAGE:if (parser->local.delimiter_branch > 1) {dbg_hid("alternative usage ignored\n");return0;}//local的usage项有扩展⽤法,它的⾼16可以定义usage_page.如果⾼16为空,它的//usage_page则定义在global中的usage_page if (item->size <= 2)data = (parser->age_page << 16) + data;//然后添加到parse->local的usage列表return hid_add_usage(parser, data);//对于有usage_min和usage_max的情况,将usage_min和usage_max之间的usage添加到//parse=>local的usage列表case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:if (parser->local.delimiter_branch > 1) {dbg_hid("alternative usage ignored\n");return0;}if (item->size <= 2)data = (parser->age_page << 16) + data;parser->age_minimum = data;return0;case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:if (parser->local.delimiter_branch > 1) {dbg_hid("alternative usage ignored\n");return0;}if (item->size <= 2)data = (parser->age_page << 16) + data;for (n = parser->age_minimum; n <= data; n++)if (hid_add_usage(parser, n)) {dbg_hid("hid_add_usage failed\n");return -1;}return0;default:dbg_hid("unknown local item tag 0x%x\n", item->tag);return0;}return0;}详细分析⼀下hid_add_usage().代码如下:static int hid_add_usage(struct hid_parser *parser, unsigned usage){if (parser->age_index >= HID_MAX_USAGES) {dbg_hid("usage index exceeded\n");return -1;}parser->age[parser->age_index] = usage;parser->local.collection_index[parser->age_index] =parser->collection_stack_ptr ?parser->collection_stack[parser->collection_stack_ptr - 1] : 0;parser->age_index++;return0;}如果usage项超过了HID_MAX_USAGES,为⾮法.最⼤为8192项.Parse->age_index表⽰local的项数,当然也表⽰了parse->age[ ]数组中的下⼀个可⽤项.parser->local.collection_index表⽰该usage所在的collection项序号.具体的collection信息存放在hid_deivce->collection[ ]中.关于collection我们在分析Main项解析的时候会详细分析.3.1.1.3:Main项解析Main项解析的⼊⼝为hid_parser_main().代码如下:static int hid_parser_main(struct hid_parser *parser, struct hid_item *item){__u32 data;int ret;//data域data = item_udata(item);switch (item->tag) {//Collectioncase HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:ret = open_collection(parser, data & 0xff);break;//End Collectioncase HID_MAIN_ITEM_TAG_END_COLLECTION:ret = close_collection(parser);break;//Inputcase HID_MAIN_ITEM_TAG_INPUT:ret = hid_add_field(parser, HID_INPUT_REPORT, data);break;//Outpputcase HID_MAIN_ITEM_TAG_OUTPUT:ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);break;//Featurecase HID_MAIN_ITEM_TAG_FEATURE:ret = hid_add_field(parser, HID_FEATURE_REPORT, data);break;default:dbg_hid("unknown main item tag 0x%x\n", item->tag);ret = 0;}memset(&parser->local, 0, sizeof(parser->local)); /* Reset the local parser environment */return ret;}对Main项的解析要稍微复杂⼀点,Main项主要有两个部份,⼀个是Collection,⼀个是Input/Output/Feature项.先来看Collection项的解析.所有的collection信息都存放在hid_device->collection[ ]中.⽽Collection项⼜有嵌套的情况,每遇到⼀个Collection项就将collection的序号⼊栈,栈为parser_device->collection_stack[ ].栈顶指针为parser_device->collection_stack_ptr .遇到了⼀个end coll 熟悉这个⼤概的情况之后,就可以跟进open_collection()了.代码如下://所有的collection都存放在hid_dev->collection 中, ⽽hid_dev->maxcollection 表⽰collection[]中的下⼀个空闲位置//paser->collection_stack[ ]存放的是当前解析的collection在hid_dev->collection[ ]中的序号static int open_collection(struct hid_parser *parser, unsigned type){struct hid_collection *collection;unsigned usage;usage = parser->age[0];//colletcion嵌套过多if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {dbg_hid("collection stack overflow\n");return -1;}//device->maxcollection:存放的collection个数//device->collection[ ]太⼩,必须扩⼤存放空间if (parser->device->maxcollection == parser->device->collection_size) {collection = kmalloc(sizeof(struct hid_collection) *parser->device->collection_size * 2, GFP_KERNEL);if (collection == NULL) {dbg_hid("failed to reallocate collection array\n");return -1;}memcpy(collection, parser->device->collection,sizeof(struct hid_collection) *parser->device->collection_size);memset(collection + parser->device->collection_size, 0,sizeof(struct hid_collection) *parser->device->collection_size);kfree(parser->device->collection);parser->device->collection = collection;parser->device->collection_size *= 2;}//将collection序号⼊栈parser->collection_stack[parser->collection_stack_ptr++] =parser->device->maxcollection;//存⼊hid_device->collection[]collection = parser->device->collection +parser->device->maxcollection++;collection->type = type;collection->usage = usage;//collection的深度collection->level = parser->collection_stack_ptr - 1;if (type == HID_COLLECTION_APPLICATION)parser->device->maxapplication++;return0;}对照上⾯的分析和函数中的注释,理解这个函数应该很简单,不做详细分析.对于Input/Output/Feature项的解析:先来看⼀下hid_device结构的定义⽚段:struct hid_device{…………struct hid_report_enum report_enum[HID_REPORT_TYPES];……}对于INPUT/OUTPUT/FEATURE,每种类型都对应report_enum[ ]中的⼀项.Struct hid_report_enum定义如下:struct hid_report_enum {unsigned numbered;struct list_head report_list;struct hid_report *report_id_hash[256];};对于每⼀个report_id,对应report_id_hash[ ]中的⼀项,同时,将所对应的hid_report添加到report_list链表中.如果有多个report_id 的情况,numbered被赋为1.Struct hid_report定义如下:struct hid_report {struct list_head list;unsigned id; /* id of this report */unsigned type; /* report type */struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */unsigned maxfield; /* maximum valid field index */unsigned size; /* size of the report (bits) */struct hid_device *device; /* associated device */}List:⽤来形成链表Id:表⽰report_idType: INPUT/OUTPUT/FEATUREField[ ]:成员列表,对应⼀个report_id有多个INPUT(OUTPUT/FEATURE)项Maxfield: field[ ]中的有效项数Size: 该report的⼤⼩Device:所属的hid_device了解了这些之后,就可以来看⼀下代码了:如下:static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags){struct hid_report *report;struct hid_field *field;int usages;unsigned offset;int i;//找到类型和对应report_id所在的report.如果不存在,则新建之if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) {dbg_hid("hid_register_report failed\n");return -1;}//对当前global数据的有效性判断。

关于linuxmtd的理解

关于linuxmtd的理解

关于linuxmtd的理解MTD 设备是象闪存芯片、小型闪存卡、记忆棒等之类的设备,它们在嵌入式设备中的使用正在不断增长。

MTD 驱动程序是在 Linux 下专门为嵌入式环境开发的新的一类驱动程序。

相对于常规块设备驱动程序,使用 MTD 驱动程序的主要优点在于 MTD 驱动程序是专门为基于闪存的设备所设计的,所以它们通常有更好的支持、更好的管理和基于扇区的擦除和读写操作的更好的接口。

Linux 下的 MTD 驱动程序接口被划分为两类模块:用户模块和硬件模块。

MTD 驱动程序设置为了访问特定的闪存设备并将文件系统置于其上,需要将 MTD 子系统编译到内核中。

这包括选择适当的 MTD 硬件和用户模块。

当前,MTD 子系统支持为数众多的闪存设备― 并且有越来越多的驱动程序正被添加进来以用于不同的闪存芯片。

有两个流行的用户模块可启用对闪存的访问:MTD_CHAR 和MTD_BLOCK 。

MTD_CHAR 提供对闪存的原始字符访问,而 MTD_BLOCK 将闪存设计为可以在上面创建文件系统的常规块设备(象IDE 磁盘)。

与MTD_CHAR 关联的设备是/dev/mtd0、mtd1、mtd2(等等),而与MTD_BLOCK 关联的设备是/dev/mtdblock0、mtdblock1(等等)。

由于 MTD_BLOCK 设备提供象块设备那样的模拟,通常更可取的是在这个模拟基础上创建象 FTL 和 JFFS2 那样的文件系统。

为了进行这个操作,可能需要创建分区表将闪存设备分拆到引导装载程序节、内核节和文件系统节中。

Linux 中 MTD 子系统的主要目标是在系统的硬件驱动程序和上层,或用户模块之间提供通用接口。

硬件驱动程序不需要知道象JFFS2 和FTL 那样的用户模块使用的方法。

所有它们真正需要提供的就是一组对底层闪存系统进行 read 、 write 和 erase 操作的简单例程。

MTD 驱动程序是专门针对嵌入式Linux的一种驱动程序,相对于常规块设备驱动程序(比如PC中的IDE硬盘)而言,MTD驱动程序能更好的支持和管理闪存设备,因为它本身就是专为闪存设备而设计的。

嵌入式linux系统分析及snmpd移植

嵌入式linux系统分析及snmpd移植

基于Linux/Net-Snmp构建DMS系统图1显示了典型的DMS系统结构图,其中中央电脑与DMS控制器之间的通信必须是基于NTCIP的。

同时,我们也可以在现场直接通过串口来控制控制器。

图1:典型的DMS系统框架在应用层,NTCIP建议使用SNMP协议来管理网络内的不同终端。

SNMP的工作模式是基于管理工作站/代理模式的。

运行网络管理程序的主机成为管理工作站,就是NTCIP网络内的中央电脑(管理中心);运行代理程序的网络设备就是我们的代理,也就是我们这里的DMS控制器。

SNMP的数据以一种标准化的层次结构进行布置。

这种强制的组织方式使数据空间既保持了通用性又保持了可扩展性。

命名的层次结构由MIB(管理信息库)组成,它是描述通过SNMP可访问的数据的结构化文本文件。

MIB包含了对特定数据变量的说明,数据变量用被称作对象标识符(OID)的名字来引用。

但是MIB只是一个给管理数据命名的约定。

SNMP名字空间和设备实际状态之间的映射关系必须由代理端代码支持才有用(包括代理的扩展开发和代理的应用程序开发)。

一、Net-Snmp在网络设备上我们使用的是基于Linux的net-snmp。

net-snmp除了提供用于响应管理站的代理程序snmpd外,还提供了一些命令行工具和一个可用于开发支持SNMP的应用程序的库组成。

在linux下通过命令行可以很方便的调用这些工具,在我们进行代理端的扩展开发时,可以使用它们来进行测试。

而开发下位机应用程序时,使用的就是该库提供的API。

下面的工作主要是在PC-Linux上完成的,在后续的工作中会逐渐的把它移植到嵌入式的开发板上。

安装完Net-Snmp后,我们需要修改代理的配置文件snmpd.conf,图2是修改前和修改后的对比。

首先ip地址的修改是指明snmpd支持的主机(即可以访问本代理的主机);把MyROGroup改成MyRWGroup,这样代理就支持了管理站对自己的写(set)操作。

mtd原理

mtd原理

mtd原理MTD原理。

MTD(Memory Technology Device)是一种基于Flash技术的存储设备,它可以被用于嵌入式系统中作为存储介质。

MTD原理是指MTD设备的工作原理和内部结构,了解MTD原理对于开发嵌入式系统和进行存储设备驱动程序的编写非常重要。

首先,我们来看一下MTD的内部结构。

MTD设备通常由Flash芯片、控制器和接口组成。

Flash芯片是存储介质,可以分为NOR Flash和NAND Flash两种类型。

NOR Flash适合做代码存储,因为它具有较快的读取速度和较低的擦写次数限制;而NAND Flash适合做数据存储,因为它具有较高的存储密度和较低的成本。

控制器负责管理Flash芯片的读写操作,包括擦除、编程和检查状态等功能。

接口则是控制器和主机系统之间的通信接口,可以是各种总线接口,如SPI、NAND Flash接口等。

MTD设备的工作原理主要包括擦除、编程和读取三个基本操作。

擦除是将Flash芯片中的数据全部清空,使其恢复到初始状态,以便进行新的数据编程。

编程是将数据写入Flash芯片中,而读取则是将数据从Flash芯片中读取出来。

这三个基本操作是MTD设备的核心功能,也是存储设备驱动程序中需要实现的功能。

在嵌入式系统中,MTD设备通常被用于存储文件系统、内核镜像、根文件系统等重要数据。

因此,对MTD设备的管理和操作具有重要意义。

在Linux系统中,MTD设备的管理和操作是通过MTD子系统来实现的。

MTD子系统提供了一组API接口,可以让应用程序和驱动程序对MTD设备进行读写操作。

同时,MTD子系统还提供了一些工具和命令,可以用于对MTD设备进行格式化、擦除、编程等操作。

总的来说,MTD原理是关于MTD设备的工作原理和内部结构的知识,它对于开发嵌入式系统和进行存储设备驱动程序的编写非常重要。

通过了解MTD原理,我们可以更好地理解MTD设备的工作机制,从而更好地应用和管理MTD设备。

mtd介绍——精选推荐

mtd介绍——精选推荐

mtd介绍MTD,Memory Technology Device即内存技术设备字符设备和块设备的区别在于前者只能被顺序读写,后者可以随机访问;同时,两者读写数据的基本单元不同。

字符设备,以字节为基本单位,在Linux中,字符设备实现的⽐较简单,不需要缓冲区即可直接读写,内核例程和⽤户态API⼀⼀对应,⽤户层的Read函数直接对应了内核中的Read例程,这种映射关系由字符设备的file_operations维护。

块设备,则以块为单位接受输⼊和返回输出。

对这种设备的读写是按块进⾏的,其接⼝相对于字符设备复杂,read、write API没有直接到块设备层,⽽是直接到⽂件系统层,然后再由⽂件系统层发起读写请求。

同时,由于块设备的IO性能与CPU相⽐很差,因此,块设备的数据流往往会引⼊⽂件系统的Cache机制。

MTD设备既⾮块设备也不是字符设备,但可以同时提供字符设备和块设备接⼝来操作它。

MTD总概述Linux中MTD的所有源码位于/drivers/mtd⼦⽬录下,MTD设备通常可分为四层这四层从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。

⼀、Flash硬件驱动层硬件驱动层负责在init时驱动Flash硬件并建⽴从具体设备到MTD原始设备映射关系tip: 映射关系通常包括分区信息、I/O映射及特定函数的映射drivers/mtd/chips : CFI/jedec接⼝通⽤驱动drivers/mtd/nand : nand通⽤驱动和部分底层驱动程序drivers/mtd/maps : nor flash映射关系相关函数drivers/mtd/devices: nor flash底层驱动⼆、MTD原始设备⽤于描述MTD原始设备的是mtd_info,它定义了⼤量的关于MTD的数据和操作函数。

mtdcore.c : MTD原始设备接⼝相关实现mtdpart.c : MTD分区接⼝相关实现三、MTD设备层基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。

linux-2.6.11.1内核移植全纪录

linux-2.6.11.1内核移植全纪录

{ name:"kernel", size:0x1d0000, offset:0x30000, }, { name:"rootfs", size:0x1600000, offset:0x200000, }, { name:"yaffsfs", size:0x2800000, offset:0x1800000, }, }; struct s3c2410_nand_set nandset={ nr_partitions:4, partitions:partition_info, }; struct s3c2410_platform_nand gyhPlatform={ tacls:0, twrph0:30, twrph1:0, sets:&nandset, nr_sets:1, }; 在s3c_device_nand结构中添加dev属性: .dev = { .platform_data = & gyhPlatform } 在arch/arm/mach-s3c2410/mach-smdk2410.c中的smdk2410_devices[]结构 体中添加&s3c_device_nand使内核在启动的时候初始化nand flash信息。
(3)修改Kconfig以允许配置 修改drivers/net/arm/目录下的Kconfig文件,在最后添加如下内容: Config ARM_CS8900 tristate "CS8900 support" depends on NET_ETHERNET && ARM && ARCH_SMDK2410 help Support for CS8900A chipset based Ethernet cards. If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from as well as . To compile this driver as a module, choose M here and read . The module will be called cs8900.o. (4)修改Makefile加入编译 修改drivers/net/arm/目录下的Makefile文件,在最后添加如下内容: obj-$(CONFIG_ARM_CS8900) += cs8900.o (5)在/arch/arm/mach-s3c2410/mach-smdk2410.c文件中,找到 smdk2410_iodesc[]结构数组,添加如下如下内容: {vSMDK2410_ETH_IO, pSMDK2410_ETH_IO, SZ_1M, MT_DEVICE} 应先添加头文件#include <asm/arch-s3c2410/smdk2410.h> 在include/asm-arm/arch-s3c2410/目录下创建smdk2410.h文件,其内容 为: #ifndef _INCLUDE_SMDK2410_H_ #define _INCLUDE_SMDK2410_H_ #include <linux/config.h> #define pSMDK2410_ETH_IO 0x19000000 #define vSMDK2410_ETH_IO 0xE0000000 #define SMDK2410_ETH_IRQ IRQ_EINT9 #endif // _INCLUDE_SMDK2410_H_ (6)make menuconfig选择CS8900A项,编译下载,CS8900可以工作 了。 6、LCD移植 (1)拷贝驱动源代码 cp s3c2410fb.h drivers/video/ cp s3c2410fb.c drivers/video/ (2)修改drivers/video目录下的Kconfig文件,在最后添加如下内容:

linux 设备树 usb相关解读

linux 设备树 usb相关解读

linux 设备树usb相关解读摘要:1.Linux设备树概述B设备在Linux系统中的识别与使用3.Linux系统中的USB设备驱动程序4.详细解析i2c-adapter和设备树的关联5.Linux系统中的USB设备管理正文:一、Linux设备树概述Linux设备树(Device Tree,简称DTS)是一种描述硬件资源的数据结构,它起源于Open Firmware项目。

在Linux系统中,设备树提供了一种标准的方式来描述硬件设备及其属性,使得操作系统能够正确地识别和管理硬件设备。

二、USB设备在Linux系统中的识别与使用在Linux系统中,USB设备的识别和使用主要依赖于设备树。

当USB设备插入电脑时,Linux系统会根据设备树中的信息来识别设备类型、驱动程序等信息。

此外,Linux系统还提供了umount、df等命令来查看和操作USB设备。

三、Linux系统中的USB设备驱动程序在Linux系统中,USB设备驱动程序负责管理和操作USB设备。

驱动程序根据设备树中的信息来初始化设备,进行数据传输和控制。

对于不同的USB设备,Linux系统提供了相应的驱动程序,如i2c-adapter。

四、详细解析i2c-adapter和设备树的关联i2c-adapter是Linux系统中一种常用的USB设备驱动程序。

在设备树中,i2c-adapter关联到i2c总线,负责管理i2c设备。

i2c-adapter的probe 部分会在/dev目录下创建文件,用于与设备进行通信。

五、Linux系统中的USB设备管理Linux系统提供了丰富的命令和工具来管理USB设备,如usb-util、lsusb 等。

用户可以通过这些命令查看USB设备的状态、信息等。

此外,Linux系统还支持通过设备树动态加载和卸载USB设备驱动程序,使得USB设备的管理更加灵活。

总结:Linux系统中的USB设备管理依赖于设备树和驱动程序。

linux mtd设备的创建流程

linux mtd设备的创建流程

linux mtd设备的创建流程创建Linux MTD 设备的流程涉及以下几个主要步骤:1. 硬件平台初始化2. 设备驱动程序开发3. MTD 设备的注册和系统挂载4. MTD 分区的创建和管理5. 文件系统的格式化和挂载下面将一步一步详细回答每个主题。

1. 硬件平台初始化:MTD(Memory Technology Device)是一种用于与特定硬件闪存设备进行交互的通用接口。

要创建MTD 设备,首先需要对硬件平台进行初始化。

此过程可能涉及的操作包括配置系统引导加载程序(如U-Boot)、启用芯片选择引脚(Chip Select)、设置时钟和引脚(GPIO)控制器等。

2. 设备驱动程序开发:MTD 驱动程序是用于将Linux 内核与硬件平台之间的通信接口。

在开发MTD 驱动程序时,首先需要确定具体的硬件设备类型以及其芯片驱动程序。

MTD 驱动程序通常使用SPI(Serial Peripheral Interface)或NAND(Not AND)总线进行通信。

针对特定的硬件设备类型,可以使用MTD API(应用程序编程接口)提供的函数来编写设备驱动程序。

3. MTD 设备的注册和系统挂载:驱动程序开发完成后,需要将MTD 设备注册到Linux 子系统中的MTD 子系统。

这涉及使用`mtd_device_register()` 函数来注册设备,并为每个设备分配设备编号。

然后,需要使用`mtd_add_partition()` 函数将设备的不同分区添加到MTD 子系统中,以便在系统启动时进行挂载。

4. MTD 分区的创建和管理:MTD 分区是将硬件闪存设备划分为逻辑部分的过程。

可以使用工具(如MTD Utils)或手动编写分区信息表(`mtdparts`)来创建MTD 分区。

分区信息表定义了每个分区的起始地址、大小、名称和权限等。

在系统启动期间,内核将使用分区信息表来将MTD 设备的不同区域映射到合适的逻辑分区。

linux2.6内核启动分析--李枝果(不看是你的损失^_^)

linux2.6内核启动分析--李枝果(不看是你的损失^_^)

S h e n z h e n F a. lizhiguo0532@ 2010-6-041Linux 2.6lizhiguo0532@ 2010-6-04----------------------------------------------------------------------------------------------------------------------/sz_farsight---------------------------------------------------------------------------------------------------------------------- ^_^SDMakefile uImageMakefile uImage *.o1. arm-linux-gnu-ld arch/arm/kernel/vmlinux.ldsarch/arm/kernel/head.o arch/arm/kernel/init_task.oS h e nz h e n F a r silizhiguo0532@ 2010-6-042 vmlinux.lds2. 3. 4. piggy.gz5.S h e ns i gh t In c . lizhiguo0532@ 2010-6-043piggy.gz piggy.o ld6. arm-linux-gnu-ld arch/arm/boot/compressed/piggy.o27 *(.piggydata) piggydata piggydata Image piggy.gzvmlinux.ldsS h e n zc . lizhiguo0532@ 2010-6-0447.8.uboot arch/arm/boot/compressed/piggy.gz- arch/arm/boot/compressed/piggy.o 0xc0008000 (arch/arm/boot/compressed/vmlinux- arch/arm/boot/zImage) 0x0 0x00000000 0x0 0x30008000 Image vmlinux 0xc0008000S h e n z h e nF a r s i g h t In c . lizhiguo0532@ 2010-6-0450x0 arch/arm/boot/compressed/head.s misc.c 1. uboot thekernelr0—>r8,r1- r7.2. LC00x0 0x300080003. 0x00x30008000 CONFIG_ZBOOT_ROM r2, r3 r5, r6, ip, sp r6 ip got4. clear bss5. cache 4K.align.section ".stack", "w"user_stack: .space 4096S h eh tI nc.lizhiguo0532@ 2010-6-0466.Zreladdr vmlinuxarch/arm/mach-s3c2410/Makefile.bootarch/arm/boot/MakefileS h e nz h e n F a r si gh t I n c .lizhiguo0532@ 2010-6-047 arch/arm/boot/compressed/Makefile ZRELADDR vmlinux ImageuImage load zImage load uImage zImage uboot zImage load entryuImage zImage mkimage uImage -a data load -e entry arch/arm/boot/Makefile0x30008000S h e n z h e n F a r s i g h t In c . lizhiguo0532@ 2010-6-048 r4 Image 0x30008000 r5 zImage r2 zImager4>=r2, r4=0x30800000 Image r2=0x30008000+(zImage+bss size)+stack size 4K + malloc size 64K zImage Image 0x30800000r4+4M<r5, r5=0x30800000 r4=30008000 zImage r4r4+4M>r5, r4=r5=0x30008000 0x30008000@ r0 = malloc end or decompress space,@ r1 = sp end or malloc begin,@ r2 = malloc end ,@ r3 = architecture IDdecompress_kernelmalloc 0x300080007. decompress_kernel in arch/arm/boot/compressed/misc.cS h e nn F a r s i gh t I n c .lizhiguo0532@ 2010-6-049Gunzip() lib/inflate.c gunzip 8. 128add r0, r0, #127bic r0, r0, #127 @ align the kernel length9 head.S 0x30008000R1 128 r2 reloc_start r3 reloc_end head.Scache_clean_flush cache cache reloc_startzImage gdb9. reloc_startS h e n z h enF a r s i g h t I n c . lizhiguo0532@ 2010-6-0410* r0 = decompressed kernel length * r1-r3 = unused* r4 = kernel execution address* r5 = decompressed kernel start* r6 = processor ID* r7 = architecture ID* r8-r14 = unused0x30008000 cache r0 r1 pc 0x30008000 /node/3VMLINUX arch/arm/kernel/head.S init/Main.c 0x0 0xC0008000Mmu I Cache D Cache r0=0 r1=architecture ID arch/arm/kernel/vmlinux.lds stext1. SVC FIR IRQ2. __lookup_processor_type cp15 cpuid .init proc_info_list cpu3. __lookup_machine_type uboot machinearchitecture number .init machine number machine_descS h e n z h n F a r si g h t I n c .lizhiguo0532@ 2010-6-0411 ……………………arch-arm-kernel-head.Sarch-arm-kernel-head/node/4start_kernel in init/Main.c1. printk(linux_banner)2. a. setup_processor()proc_info_list list cpu_name cpuname idproc_arch system_utsname= list->arch_name armv4telf_platform= list->elf_name v4 elf_hwcap = list->elf_hwcap;/* 1|2|4 */ cpu_proc_init()Cpu-single.h#define cpu_proc_init __cpu_fn(CPU_NAME,_proc_init)#define __cpu_fn(name,x) __catify_fn(name,x)#define __catify_fn(name,x) name##xCPU_NAME = cpu_arm920cpu_proc_init(); cpu_arm920_proc_init()proc_arm920.S b. mdesc = setup_machine(machine_arch_type).init machine_desclist list-namec. machine_name = mdesc->name machine_named. tags = phys_to_virt(mdesc->boot_params)uboot 0x300001000xc0000100e.if (tags->hdr.tag == ATAG_CORE) {if (meminfo.nr_banks != 0) /* meminfo defined in setup.c */squash_mem_tags(tags);parse_tags(tags);}static struct meminfo meminfo __initdata = { 0, };in steup.cparse_tags(tags) in steup.cS h e n z h e n F a r s i g h t In clizhiguo0532@ 2010-6-0412Steup_arch()- parse_tags()- parse_tag(), all function in steup.c __tagtable_begin, __tagtable_end arch/arm/kernel/vmlinux.ldsparse_tag()parseIgnoring unrecognised tag 0x%08x\n ubootf. struct mm_struct init_mm = INIT_MM(init_mm);init_mm.start_code = (unsigned long) &_text;init_mm.end_code = (unsigned long) &_etext;init_mm.end_data = (unsigned long) &_edata;init_mm.brk = (unsigned long) &_end;_text, _etext, _edata, _end arch/arm/kernel/vmlinux.ldsg. parse_cmdline(cmdline_p, from) uboot commond_linefrom command_linecommand_linestart_kernerl mem initrdstart_kernel()->parse_option() mem initrdcommand_line *cmdline_pstart_kernelmem initrdh. paging_init(&meminfo, mdesc);/**/ in arch/arm/mm/init.cmemtable_init(mi)/bbstcon,board,Embedded,reid,1165977462.html0xffff0000mdesc->map_io() smdk2410arch/arm/mach-s3c2410/mach-smdk2410.c Linux , smdk2410_map_io() 1 iotable_init(s3c_iodesc, GPIO,IRQ,MEMCTRL,UARTS h e nz h e n F a r s i gh t In c . lizhiguo0532@ 2010-6-0413 2 (cpu->map_io)(mach_desc, size) LCD, map.h S3C2410_ADDR(x) ((void __iomem *)0xF0000000 + (x)) IO 0xF0000000 1M 1. GPIO IRQ UART MEMCRTL WATCHDOG USB 2. kmalloc 0x30008000 phys_to_virt virt_to_ phys 3. mmu TTB 0x30004000 16K mmu arch/arm/kernel/head.S 4M mmu sector Uarth.request_standard_resources(&meminfo, mdesc) memory kernel_text kernel_data video_ram new new resource NULLi. cpu_init() in arch/arm/kernel/setup.ccpu cpu id cache IRQ ABT UND stack 12 svci. __mach_desc_SMDK2410_typeinit_arch_irq init_machine system_timer3. sched_init()init_idle (current, smp_processor_id()) idle4. preempt_disable()5. (zone) build_all_zonelists()6. printk(KERN_NOTICE "Kernel command line: %s\n", saved_command_line); uboot command_line setup_arch7. parse_early_param()S h e n z h e n F a r s i g h t In c . lizhiguo0532@ 2010-6-0414 __setup setup_arch &command_line command_linesaved_command_line8. parse_args("Booting kernel", command_line, __start___param,__stop___param - __start___param,&unknown_bootoption);parse_early_param(); setup paramcommand_line setup_arch__start___param param System.map__stop___param - __start___paramunknown_bootoption paramparse_one()parse_one() param __setupLinux bootargs9. sort_main_extable()__start___ex_table __stop___ex_table *(__ex_table) struct exception_table_entry insn10. setup_arch- paging_init- memtable_initinit_maps, alloc_bootmem_low_pages ARM 0xFFFF0000 0xFFFF0000trap_init .Lcvectors 0xffff0000 __stubs_start __stubs_end 0xffff0200 0xffff0500 0xffff0000 cache DOMAIN_USER DOMAIN_MANAGER DOMAIN_CLIENT11.rcu_init() cpu struct rcu_dataper_cpu_rcu_data per_cpu_rcu_bh_data.12.init_IRQstruct irqdesc irq_desc[NR_IRQS], irq_desc[n] bad_irq_desc pendS h e nz h e n F a r s i gh t I n c . lizhiguo0532@ 2010-6-0415 init_arch_irq setup_arch smdk2410_init_irq in mach-smdk2410.cs3c24xx_init_irq do_level_IRQ do_edge_IRQ __do_irquart ADC13.pidhash_init()pid_hash hash pidhash_shift pidhash_shift min 12 hash hash pid_hash[n](n=1~3), hash hash struct hlist_head first NULL14.init_timers()struct tvec_t_base_s per_cpu_tvec_bases, per_cpu_tvec_bases lock15.softirq_initS h e n z h e n F a r s i gh t I n c .lizhiguo0532@ 2010-6-041616.time_init() timersetup_archtime_init if s3c2410_timer_init timer417.console_initprintk log_bufa. tty_register_ldisc TTYtty ttyttytty ttyS h e n z h e n F ar s i g h t I n c . lizhiguo0532@ 2010-6-0417 ppp tty tty b. s3c24xx_serial_initconsole /*vmlinux.lds.S__con_initcall_start = .;*(.con_initcall.init)__con_initcall_end = .;con_initcall.initfn .con_initcall.init :#define console_initcall(fn) \static initcall_t __initcall_##fn \__attribute_used____attribute__((__section__(".con_initcall.init")))=fn*///:console_initcall(s3c24xx_serial_initconsole);//:start_kernel->console_init->s3c24xx_serial_initconsolecall = __con_initcall_start; /* console_initcall */while (call < __con_initcall_end) {(*call)();call++;}18.profile_init()/* *///profile// bootargs profile/*profile menuconfig profiling support1. profileprofile profile=1 profile=schedule 12. /proc/profile readprofilereadprofile -m /proc/kallsyms | sort -nr > ~/cur_profile.log,readprofile -r -m /proc/kallsyms |sort -nr,readprofile -r && sleep 1 && readprofile -m /proc/kallsymsS h e n z h e n F a r s i g h t In c . lizhiguo0532@ 2010-6-0418|sort -nr >~/cur_profile.log 3. /proc/profile profile profile=?profile=schedule ? schedule schedule*/19.local_irq_enable()IRQ20.mem_init()alloc_bootmem(),alloc_bootmem_low(),alloc_bootmem_pages()21.kmem_cache_init()slab22. numa_policy_init();if (late_time_init)late_time_init();calibrate_delay();// BogMIPS23. pidmap_init();pgtable_cache_init();prio_tree_init();/*index_bits_to_maxindex[BITS_PER_LONG]index_bits_to_maxindex[n] -1index_bits_to_maxindex[BITS_PER_LONG-1] ~0UL*/24 anon_vma_init();/*kmem_cache_creat() struct anon_vmakmem_cache_t anon_vma ,void anon_vma_ctor NULLkmem_cache_t anon_vma_chachepS h e n z h e n F a r s i g h tlizhiguo0532@ 2010-6-0419 */ 25. fork_init(num_physpages);/* */ 26. proc_caches_init();buffer_init();/*kmem_cache_create("buffer_head",sizeof(struct buffer_head), 0,SLAB_RECLAIM_ACCOUNT|SLAB_PANIC, init_buffer_head, NULL) struct buffer_head kmem_cache_t */27. security_init();/* */28. vfs_caches_init(num_physpages);radix_tree_init();signals_init();kmem_cache_create("sigqueue",sizeof(struct sigqueue),__alignof__(struct sigqueue),SLAB_PANIC, NULL, NULL) struct sigqueue kmem_cache_t sigqueue cache line kmem_cache_t sihqueue_cachep.29. page_writeback_init()buffer_pages.30. proc_root_init();/* proc CONFIG_PROC_FS */31. check_bugs();/* arm */32 rest_init()initlinux :0 rest_init()a. in arch/arm/kernel/process.cinitb. schedule() idle schedulec. cpu_idle()0S h e n z h e n F a r s i g h t In c . lizhiguo0532@ 2010-6-0420 init in main.c a. lock_kernel() lock b. smpc. populate_rootfs() initcallsd. do_basic_setup()#define module_init(x) __initcall(x);#define __initcall(fn) device_initcall(fn)#define core_initcall(fn) __define_initcall("1",fn)#define postcore_initcall(fn) __define_initcall("2",fn) #define arch_initcall(fn) __define_initcall("3",fn) #define subsys_initcall(fn) __define_initcall("4",fn) #define fs_initcall(fn) __define_initcall("5",fn) #define device_initcall(fn) __define_initcall("6",fn) #define late_initcall(fn) __define_initcall("7",fn)#define __define_initcall(level,fn) \static initcall_t __initcall_##fn __attribute_used__ \__attribute__((__section__(".initcall" level ".init"))) = fn include/linux/init.harch_initcall initcall module_init init 1 do_basic_setup do_initcalls()S h e n z h e n F a r s i gh t I n c . lizhiguo0532@ 2010-6-0421 Start_kernel * -- rest_init()* -- kernel_thread()* init 1 -- Init* -- populate_rootfs() -- do_basic_setup()* initcall modlue_init -- init_workqueues() -- usermodehelper_init() khelper-- driver_init()-- sysctl_init()-- sock_init() socket-- do_initcalls()*-- (*call)()* initcall-- prepare_namespace()-- name_to_dev_t()root bootargs root=/dev/** root=31:03 /dev/mtdblock3 root /dev/ 31-- mount_root();/* */-- free_initmem() init:Freeing init memory: 116K-- sys_open() and sys_dup(0) 0 1 2-- run_init_process(execute_command) init=/initrd command_line-- cpu_idle() 0init 1 …… run_init_process("/sbin/init");run_init_process("/etc/init");run_init_process("/bin/init");run_init_process("/bin/sh");……S h n n F a r s i g h t In c . lizhiguo0532@ 2010-6-0422 ARM Linux -- -PXA255--- Linux bootargs/u3/99423/article.html/bbstcon,board,Embedded,reid,1165977462.html/node/4。

第六讲-MTD驱动

第六讲-MTD驱动
性能比较
NOR的读速度比NAND稍快一些 NAND的写入速度比NOR快很多 NAND的擦除速度远比NOR的快 大多数写入操作需要先进行擦除操作 NAND的擦除单元更小,相应的擦除电路更少
Flash存储器简介
接口差别
NOR flash带有SRAM接口,线性寻址,可以很容易 地存取其内部的每一个字节 NAND flash使用复用接口和控制I/O多次寻址存取数 据 NAND读和写操作采用512字节的块,这一点有点像 硬盘管理,此类操作易于取代硬盘等类似的块设备
当count>0 时{ 裁减本次操作大小len 至min(MAX_KMALLOC_SIZE,count), 申请一块大小为MAX_KMALLOC_SIZE 的内核空间kbuf, 调用mtd_info->read 将MTD 设备中的数据读入kbuf, 将kbuf 中的数据拷贝到用户空间buf, count 自减 释放kb设备
Mtd_write
mtd_write 直接直接调用mtd_info 的write 函数,因此, 字符设备接口跳过了patition 这一层。
当count>0 时{ 裁减本次操作大小len 至min(MAX_KMALLOC_SIZE,count), 申请一块大小为MAX_KMALLOC_SIZE 的内核空间kbuf, 将用户空间buf 中的数据拷贝到kbuf, 调用mtd_info->write 将kbuf 中的数据读入MTD 设备, count 自减 释放kbuf }
MTD (字符/块)设备层
基于MTD 原始设备,Linux 系统可以定义出 MTD 的块设备(主设备号31)和字符设备 (设备号90),构成MTD 设备层。
MTD 字符设备的定义在mtdchar.c 中实现,通过注 册一系列file_operation 函数(lseek、open、close、 read、write、ioctl)可实现对MTD 设备的读写和控 制。 MTD块设备则是定义了一个描述MTD 块设备的结 构mtdblk_dev,并声明了一个名为mtdblks 的指针 数组,这数组中的每一个mtdblk_dev 和mtd_table 中的每一个mtd_info一一对应。

Linux内核调试

Linux内核调试

可以看到,jiffies 的值得到了更新。
1.7.2 调试模块
由于模块并没有作为 vmlinux 的一部分传给 gdb,因此必须通过某种方法把模块信息告 知 gdb。 1.模块文件的组成
Linux 的模块是 ELF 格式的可执行映像,分成了很多个 section。与调试关系较为密切的
的三个 section 如下: .text:包含了模块的可执行代码。 .bss 和.data:包括了模块的变量(在模块编译阶段被初始化的变量在.data,其他的 在.bss )
(1)启动 gdb。
在第一行调用 gdb 时所传入的参数中: vmlinux:未压缩的 ELF 内核可执行文件变量。 此时可以通过 p 命令查看系统变量,例如
在没有选中 CONFIG_DEBUG_IN FO 时也可查看 jiffies
需要注意的是,从上图中可以看出,虽然 jiffies 是不停变换的,但是 gdb 每次读取同一 个变量时将得到相同的值,这是因为 gdb 对读到的值进行了缓存。如果希望去掉缓存的影响, 可以使用 core-file 命令。
(1)如果不清楚当前正在运行的内核源代码的目录,可以通过如下方法查看。
(2)进入内核源代码所在目录,通过 make menuconfig 命令进入编译选项配置环境, 如图 1.1 所示。
调试内核
图 1.1 编译选项配置环境
在选项配置环境中每个选项有“*”(编译进内核)、“M”(以模块方式编译)和“”(不 编译)三种状态,可以分别使用“Y”、“M”和“N”键来设置。 2.配置编译选项
(2)加载模块信息。模块名称和.text 基址是 add-symbol-file 命令的必要参数,.bss 和.data 的基址可使用-s 选项传给 add-symbol-file 命令。

mtdinfo 使用方法

mtdinfo 使用方法

mtdinfo 使用方法
mtdinfo是一个用于显示Memory Technology Device (MTD) 子系统信息的命令行工具。

MTD是用于嵌入式系统中的闪存设备和其他存储设备的子系统。

要使用mtdinfo命令,您需要在Linux系统上安装MTD子系统,并且具有相应的权限。

mtdinfo命令的基本语法是:
mtdinfo [选项]
下面是一些常用的选项:
-a, --all,显示所有的MTD设备信息。

-i, --id,显示MTD设备的ID信息。

-n, --name,显示MTD设备的名称。

-s, --size,显示MTD设备的大小信息。

-t, --type,显示MTD设备的类型信息。

要显示所有MTD设备的信息,您可以简单地在终端中输入以下命令:
mtdinfo -a.
如果您只想显示特定MTD设备的信息,可以使用其他选项,例如:
mtdinfo -i /dev/mtd0。

此命令将显示/dev/mtd0设备的ID信息。

另外,您还可以使用mtdinfo命令来获取有关MTD设备的更多详细信息,比如擦除块大小、页大小等。

这些信息对于开发嵌入式系统和处理闪存设备非常有用。

总的来说,mtdinfo命令是一个强大的工具,可以帮助您了解系统中闪存设备的详细信息,以及进行诊断和调试。

当然,在使用mtdinfo命令时,请确保您具有足够的权限以及了解您正在操作的设备,以免造成意外损坏。

希望这些信息能帮助到您。

精选嵌入式LINUX设备驱动程序课件

精选嵌入式LINUX设备驱动程序课件

设备的控制操作
对设备的控制操作可通过文件操作数据结构中的ioctl()函数来完成。控制操作与具体的设备有密切关系,需要根据设备实际情况进行具体分析。
设备的轮询和中断处理
轮询方式对于不支持中断的硬件设备,读写时需要轮流查询设备的状态,以便决定随后的数据操作。如果轮询处理方式的驱动程序被链接到内核,则意味着查询过程中,内核一直处于闲置状态。解决办法是使用内核定时器,进行定期查询。
主设备号与次设备号
次设备号用于标识使用同一设备驱动程序的不同硬件,并仅由设备驱动程序解释 当应用程序操作某个设备文件时,Linux内核根据其主设备号调用相应的驱动程序,并从用户态进入内核态驱动程序判断次设备号,并完成相应的硬件操作。
用户空间和内核空间
Linux运行在2种模式下内核模式用户模式内核模式对应内核空间,而用户模式对应用户空间。驱动程序作为内核的一部分,它对应内核空间,应用程序不能直接访问其数据,
帧缓冲设备驱动程序
LCD分类
LCD可由为液晶照明的方式有两种:传送式和反射式传送式屏幕要使用外加光源照明,称为背光(backlight),照明光源要安装在LCD的背后。传送式LCD在正常光线及暗光线下,显示效果都很好,但在户外,尤其在日光下,很难辩清显示内容。 反射式屏幕,则不需要外加照明电源,使用周围环境的光线(或在某些笔记本中,使用前部照明系统的光线)。这样,反射式屏幕就没有背光,所以,此种屏幕在户外或光线充足的室内,才会有出色的显示效果,但在一般室内光线下,这种显示屏的显示效果就不及背光传送式的。
文件操作结构体的主要函数
open: 用于打开文件设备release: 在关闭文件的调用read: 用于从设备中读取数据write: 向设备发送数据poll: 查询设备是否可读或可写ioctl: 提供执行设备特定命令的方法fasync: 用于设备的异步通知操作

linux-2.6.21内核中建立jffs2文件系统(mtd分区的使用)

linux-2.6.21内核中建立jffs2文件系统(mtd分区的使用)

修改Makefile如下:
CC=arm-linux-gcc
LDSHARED=arm-linux-ld -shared
$ make all
$ make install
注意:这里是安装在/usr/local/arm/3.4.1/arm-linux目录下
由于交叉编译mtd工具时需要zlib.h文件,所以在编译之前先安装zlib库文件。
[ ] Debugging
<*> MTD concatenating support
[*] MTD partitioning support
< > RedBoot partition table parsing
[*] Command line partition table parsing
# flash_erase /dev/mtd1
制作jffs2映像
# cd /var/tmp
Hale Waihona Puke # mkdir jffs2 (jffs2下的目录可以任意建)
# mkfs.jffs2 –d jffs2/ -o jffs2.img
# cp /var/tmp/jffs2/jffs2.img /dev/mtdblock1
a.通过nand编程器烧写镜像文件到nand flash芯片然后再焊接到pcb.
b.挂载nfs文件系统,使用nandwrite工具写镜像文件
到mtd设备。
第一种适合生产时使用,而我们测试时候比较适合使用第二种方式。
挂载nfs文件系统:
nandwrite –o /dev/mtd1 target.jffs2
Miscellaneous filesystems
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Linux 2.6.11 MTD驱动情景分析02/13 2008最近几天为了熟悉linux的驱动开发,我选择了MTD驱动做了一些研究。

我能找到的文章中我觉得有些部分不够细致,所以我还是自己写了一部分分析,希望对别人也能有所帮助,也做为自己的一个备忘。

蓝色文字的部分是从网络上摘录的。

一个嵌入式系统经常会使用NOR flash 或NAND flash来存放bootload,内核和文件系统等等。

下面是网络上找到的linux下的mtd驱动的分析:一、Flash硬件驱动层:硬件驱动层负责在init时驱动Flash硬件,Linux MTD 设备的NOR Flash芯片驱动遵循CFI接口标准,其驱动程序位于drivers/mtd/chips子目录下。

NAND型Flash的驱动程序则位于/drivers/mtd/nand 子目录下。

二、MTD原始设备:原始设备层有两部分组成,一部分是MTD原始设备的通用代码,另一部分是各个特定的Flash的数据,例如分区。

用于描述MTD原始设备的数据结构是mtd_info,这其中定义了大量的关于MTD的数据和操作函数。

mtd_table(mtdcore.c)则是所有MTD原始设备的列表,mtd_part(mtd_part.c)是用于表示MTD原始设备分区的结构,其中包含了mtd_info,因为每一个分区都是被看成一个MTD原始设备加在mtd_table中的,mtd_part.mtd_info中的大部分数据都从该分区的主分区mtd_part- >master 中获得。

在drivers/mtd/maps/子目录下存放的是特定的flash的数据,每一个文件都描述了一块板子上的flash。

其中调用add_mtd_device()、del_mtd_device()建立/删除mtd_info结构并将其加入/删除mtd_table(或者调用add_mtd_partition()、del_mtd_partition()(mtdpart.c)建立/删除mtd_part结构并将mtd_part.mtd_info加入/删除mtd_table 中)。

三、MTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。

MTD字符设备的定义在mtdchar.c中实现,通过注册一系列file operation函数(lseek、open、close、read、write)。

MTD块设备则是定义了一个描述MTD块设备的结构mtdblk_dev,并声明了一个名为mtdblks的指针数组,该数组中的每一个mtdblk_dev和mtd_table中的每一个mtd_info一一对应。

四、设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和MTD块设备节点(主设备号为31),通过访问此设备节点即可访问MTD字符设备和块设备。

五、根文件系统:在Bootloader中将JFFS(或JFFS2)的文件系统映像jffs.image (或jffs2.img)烧到flash的某一个分区中,在/arch/arm/mach-your/arch.c文件的your_fixup函数中将该分区作为根文件系统挂载。

六、文件系统:内核启动后,通过mount 命令可以将flash中的其余分区作为文件系统挂载到mountpoint上。

NOR型Flash芯片驱动与MTD原始设备所有的NOR型Flash的驱动(探测probe)程序都放在drivers/mtd/chips 下,一个MTD原始设备可以由一块或者数块相同的Flash芯片组成。

假设由4块devicetype为x8的Flash,每块大小为8M,interleave为2,起始地址为0x01000000,地址相连,则构成一个MTD原始设备(0x01000000-0x03000000),其中两块interleave成一个chip,其地址从0x01000000到0x02000000,另两块interleave成一个chip,其地址从0x02000000到0x03000000。

请注意,所有组成一个MTD原始设备的Flash芯片必须是同类型的(无论是interleave还是地址相连),在描述MTD原始设备的数据结构中也只是采用了同一个结构来描述组成它的Flash芯片。

每个MTD原始设备都有一个mtd_info结构,其中的priv指针指向一个map_info结构,map_info结构中的fldrv_priv指向一个cfi_private结构,cfi_private结构的cfiq指针指向一个cfi_ident结构,chips指针指向一个flchip 结构的数组。

其中mtd_info、map_info和cfi_private结构用于描述MTD原始设备;因为组成MTD原始设备的NOR型Flash相同,cfi_ident结构用于描述Flash芯片的信息;而flchip结构用于描述每个Flash芯片的专有信息(比如说起始地址)总的来说,嵌入式系统中一般来说会有一块或多块连续的NOR flash或NAND flash空间(每一个可能是多块相同的芯片来构成)每一个这样的空间被看成一个MTD原始设备(我不知道这个名字谁起的,我也这么用吧)根据一些文章和代码中使用的变量名,我后面称呼它为主分区。

你可以按照自己的需要把主分区分成几个区,我的开发板用的分区信息如下:来自alchemy_flash.c:static struct mtd_partition alchemy_partitions[] = {{.name = "User FS", //这里给根文件系统.size = BOARD_FLASH_SIZE - 0x00400000,.offset = 0x0000000},{.name = "YAMON",//这块给bootloader.size = 0x0100000,.offset = MTDPART_OFS_APPEND, //表示接着上一个分区.mask_flags = MTD_WRITEABLE},{.name = "raw kernel",.size = (0x300000 - 0x40000), /* last 256KB is yamon env */ //这块给自解压//的压缩内核,最后留了点给booterloader的环境变量,它没有被设备驱动使用,而是由booterloader以自己的方式访问。

.offset = MTDPART_OFS_APPEND,}};如果你增加或者是减少了你的flash空间(通过增加或减少flash芯片)或则你想调整几个分区的大小,你只需要修改这个表就可以了。

如果你还有一块NAND区,那么你可能有如下的分区表(au1550nd.c):const static struct mtd_partition partition_info[] = {{.name = "NAND FS 0",.offset = 0,.size = 8*1024*1024},{.name = "NAND FS 1",.offset = MTDPART_OFS_APPEND,.size = MTDPART_SIZ_FULL}};整个alchemy_flash.c就两个函数:alchemy_mtd_init(void)和alchemy_mtd_cleanup()。

int __init alchemy_mtd_init(void){struct mtd_partition *parts;int nb_parts = 0;unsigned long window_addr;unsigned long window_size;/* Default flash buswidth */alchemy_map.bankwidth = BOARD_FLASH_WIDTH;window_addr = 0x20000000 - BOARD_FLASH_SIZE;window_size = BOARD_FLASH_SIZE;#ifdef CONFIG_MIPS_MIRAGE_WHY/* Boot ROM flash bank only; no user bank */window_addr = 0x1C000000;window_size = 0x04000000;/* USERFS from 0x1C00 0000 to 0x1FC00000 */alchemy_partitions[0].size = 0x03C00000;#endif/** Static partition definition selection*/parts = alchemy_partitions;nb_parts = NB_OF(alchemy_partitions);alchemy_map.size = window_size;/** Now let's probe for the actual flash. Do it here since* specific machine settings might have been set above.*/printk(KERN_NOTICE BOARD_MAP_NAME ": probing %d-bit flash bus\n",alchemy_map.bankwidth*8);alchemy_map.virt = ioremap(window_addr, window_size);mymtd = do_map_probe("cfi_probe", &alchemy_map);if (!mymtd) {iounmap(alchemy_map.virt);return -ENXIO;}mymtd->owner = THIS_MODULE;add_mtd_partitions(mymtd, parts, nb_parts);return 0;}看看红色的区域,do_map_probe返回了一个mtd_info结构指针。

那么表明这个函数在正确找到你的驱动(cfi驱动)后会填好这个表把其中的读写函数等设置到正确的值,具体的实现放到以后分析吧,其中的一个map_info参数,我暂时没有完全读懂,因为我以前不曾研究flash的底层驱动,你需要设置好bankwidth和基地址,和驱动名字,我只能通过一些信息猜测他用来管理这个主分区并给底层驱动使用的,如坏块信息就保存在这里。

相关文档
最新文档