Linux USB 鼠标驱动程序详解
USB 总线引出两个重要的链表!
一个USB 总线引出两个重要的链表,一个为USB 设备链表,一个为USB 驱动链表。设备链表包含各种系统中的USB 设备以及这些设备的所有接口,驱动链表包含USB 设备驱动程序(usb device driver)和USB 驱动程序(usb driver)。
USB 设备驱动程序(usb device driver)和USB 驱动程序(usb driver)的区别是什么?
USB 设备驱动程序包含USB 设备的一些通用特性,将与所有USB 设备相匹配。在USB core 定义了:struct usb_device_driver usb_generic_driver。usb_generic_driver 是USB 子系统中唯一的一个设备驱动程序对象。而USB 驱动程序则是与接口相匹配,接口是一个完成特定功能的端点的集合。
设备是如何添加到设备链表上去的?
在设备插入USB 控制器之后,USB core 即会将设备在系统中注册,添加到USB 设备链表上去。
USB 设备驱动程序(usb device driver)是如何添加到驱动链表上去的?
在系统启动注册USB core 时,USB 设备驱动程序即将被注册,也就添加到驱动链表上去了。
接口是如何添加到设备链表上去的?
在USB 设备驱动程序和USB 设备的匹配之后,USB core 会对设备进行配置,分析设备的结构之后会将设备所有接口都添加到设备链表上去。比如鼠标设备中有一个接口,USB core 对鼠标设备配置后,会将这个接口添加到设备链表上去。
USB 驱动程序(usb driver)是如何添加到驱动链表上去的?
在每个USB 驱动程序的被注册时,USB 驱动程序即会添加到驱动链表上去。比如鼠标驱动程序,usb_mouse_init 函数将通过usb_register(&usb_mouse_driver) 将鼠标驱动程序注册到USB core 中,然后就添加到驱动链表中去了。其中usb_mouse_driver 是描述鼠标驱动程序的结构体。
已配置状态(configured status)之后话
当鼠标的设备、接口都添加到设备链表,并且鼠标驱动程序也添加到驱动链表上去了,系统就进入一种叫做已配置(configured)的状态。要达到已配置状态,将经历复杂的过程,USB core 为USB 设备奉献着无怨无悔。在这个过程中,系统将会建立起该设备的的设备、配置、接口、设置、端点的描述信息,它们分别被usb_device、usb_configuration、usb_interface、
usb_host_interface、usb_host_endpoint 结构体描述。
设备达到已配置状态后,首先当然就要进行USB 驱动程序和相应接口的配对,对于鼠标设备来说则是鼠标驱动程序和鼠标中的接口的配对。USB core 会调用usb_device_match 函数,通过比较设备中的接口信息和USB 驱动程序中的id_table,来初步决定该USB 驱动程序是不是跟相应接口相匹配。通过这一道关卡后,USB core 会认为这个设备应该由这个驱动程序负责。
然而,仅仅这一步是不够的,接着,将会调用USB 驱动程序中的probe 函数对相应接口进行进一步检查。如果该驱动程序确实适合设备接口,对设备做一些初始化工作,分配urb 准备数据传输。
当鼠标设备在用户空间打开时,将提交probe 函数构建的urb 请求块,urb 将开始为传送数据而忙碌了。urb 请求块就像一个装东西的“袋子”,USB 驱动程序把“空袋子”提交给USB core,然后再交给主控制器,主控制器把数据放入这个“袋子”后再将装满数据的“袋子”通过USB core 交还给USB 驱动程序,这样一次数据传输就完成了。
以下是完全注释后的鼠标驱动程序代码usbmouse.c
view plaincopy to clipboardprint?
/*
* $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $
*
* Copyright (c) 1999-2001 V ojtech Pavlik
*
* USB HIDBP Mouse support
*/
#include
#include
#include
#include
#include
#include
/*
* Version Information
*/
#define DRIVER_VERSION "v1.6"
#define DRIVER_AUTHOR "V ojtech Pavlik "
#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
#define DRIVER_LICENSE "GPL"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
/*
* 鼠标结构体,用于描述鼠标设备。
*/
struct usb_mouse
{
/* 鼠标设备的名称,包括生产厂商、产品类别、产品等信息*/
char name[128];
/* 设备节点名称*/
char phys[64];
/* USB 鼠标是一种USB 设备,需要内嵌一个USB 设备结构体来描述其USB 属性*/
struct usb_device *usbdev;
/* USB 鼠标同时又是一种输入设备,需要内嵌一个输入设备结构体来描述其输入设备的属性*/
struct input_dev *dev;
/* URB 请求包结构体,用于传送数据*/
struct urb *irq;
/* 普通传输用的地址*/
signed char *data;
/* dma 传输用的地址*/
dma_addr_t data_dma;
};
/*
* urb 回调函数,在完成提交urb 后,urb 回调函数将被调用。
* 此函数作为usb_fill_int_urb 函数的形参,为构建的urb 制定的回调函数。
*/
static void usb_mouse_irq(struct urb *urb)
{
/*
* urb 中的context 指针用于为USB 驱动程序保存一些数据。比如在这个回调函数的形参没有传递在probe
* 中为mouse 结构体分配的那块内存的地址指针,而又需要用到那块内存区域中的数据,context 指针则帮了
* 大忙了!
* 在填充urb 时将context 指针指向mouse 结构体数据区,在这又创建一个局部mouse 指针指向在probe
* 函数中为mouse 申请的那块内存,那块内存保存着非常重要数据。
* 当urb 通过USB core 提交给hc 之后,如果结果正常,mouse->data 指向的内存区域将保存着鼠标的按键
* 和移动坐标信息,系统则依靠这些信息对鼠标的行为作出反应。
* mouse 中内嵌的dev 指针,指向input_dev 所属于的内存区域。
*/
struct usb_mouse *mouse = urb->context;
signed char *data = mouse->data;
struct input_dev *dev = mouse->dev;
int status;
/*
* status 值为0 表示urb 成功返回,直接跳出循环把鼠标事件报告给输入子系统。
* ECONNRESET 出错信息表示urb 被usb_unlink_urb 函数给unlink 了,ENOENT 出错信息表示urb 被
* usb_kill_urb 函数给kill 了。usb_kill_urb 表示彻底结束urb 的生命周期,而usb_unlink_urb 则
* 是停止urb,这个函数不等urb 完全终止就会返回给回调函数。这在运行中断处理程序时或者等待某自旋锁
* 时非常有用,在这两种情况下是不能睡眠的,而等待一个urb 完全停止很可能会出现睡眠的情况。
* ESHUTDOWN 这种错误表示USB 主控制器驱动程序发生了严重的错误,或者提交完urb 的一瞬间设备被拔出。
* 遇见除了以上三种错误以外的错误,将申请重传urb。
*/
switch (urb->status)
{
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
/*
* 向输入子系统汇报鼠标事件情况,以便作出反应。
* data 数组的第0个字节:bit 0、1、2、3、4分别代表左、右、中、SIDE、EXTRA键的按下情况;
* data 数组的第1个字节:表示鼠标的水平位移;
* data 数组的第2个字节:表示鼠标的垂直位移;
* data 数组的第3个字节:REL_WHEEL位移。
*/
input_report_key(dev, BTN_LEFT, data[0] & 0x01);
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
input_report_key(dev, BTN_SIDE, data[0] & 0x08);
input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
input_report_rel(dev, REL_X, data[1]);
input_report_rel(dev, REL_Y, data[2]);
input_report_rel(dev, REL_WHEEL, data[3]);
/*
* 这里是用于事件同步。上面几行是一次完整的鼠标事件,包括按键信息、绝对坐标信息和滚轮信息,输入子
* 系统正是通过这个同步信号来在多个完整事件报告中区分每一次完整事件报告。示意如下:
* 按键信息坐标位移信息滚轮信息EV_SYC | 按键信息坐标位移信息滚轮信息EV_SYC ...
*/
input_sync(dev);
/*
* 系统需要周期性不断地获取鼠标的事件信息,因此在urb 回调函数的末尾再次提交urb 请求块,这样又会
* 调用新的回调函数,周而复始。
* 在回调函数中提交urb 一定只能是GFP_ATOMIC 优先级的,因为urb 回调函数运行于中断上下文中,在提
* 交urb 过程中可能会需要申请内存、保持信号量,这些操作或许会导致USB core 睡眠,一切导致睡眠的行
* 为都是不允许的。
*/
resubmit:
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status)
err ("can’t resubmit intr, %s-%s/input0, status %d",
mouse->usbdev->bus->bus_name,
mouse->usbdev->devpath, status);
}
/*
* 打开鼠标设备时,开始提交在probe 函数中构建的urb,进入urb 周期。
*/
static int usb_mouse_open(struct input_dev *dev)
{
struct usb_mouse *mouse = dev->private;
mouse->irq->dev = mouse->usbdev;
if (usb_submit_urb(mouse->irq, GFP_KERNEL))
return -EIO;
return 0;
}
/*
* 关闭鼠标设备时,结束urb 生命周期。
*/
static void usb_mouse_close(struct input_dev *dev)
{
struct usb_mouse *mouse = dev->private;
usb_kill_urb(mouse->irq);
}
/*
* 驱动程序的探测函数
*/
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
/*
* 接口结构体包含于设备结构体中,interface_to_usbdev 是通过接口结构体获得它的设备结构体。
* usb_host_interface 是用于描述接口设置的结构体,内嵌在接口结构体usb_interface 中。
* usb_endpoint_descriptor 是端点描述符结构体,内嵌在端点结构体usb_host_endpoint 中,而端点
* 结构体内嵌在接口设置结构体中。
*/
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe, maxp;
interface = intf->cur_altsetting;
/* 鼠标仅有一个interrupt 类型的in 端点,不满足此要求的设备均报错*/
if (interface->desc.bNumEndpoints != 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
/*
* 返回对应端点能够传输的最大的数据包,鼠标的返回的最大数据包为4个字节,数据包具体内容在urb
* 回调函数中有详细说明。
*/
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
/* 为mouse 设备结构体分配内存*/
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
/* input_dev */
input_dev = input_allocate_device();
if (!mouse || !input_dev)
goto fail1;
/*
* 申请内存空间用于数据传输,data 为指向该空间的地址,data_dma 则是这块内存空间的dma 映射,
* 即这块内存空间对应的dma 地址。在使用dma 传输的情况下,则使用data_dma 指向的dma 区域,
* 否则使用data 指向的普通内存区域进行传输。
* GFP_ATOMIC 表示不等待,GFP_KERNEL 是普通的优先级,可以睡眠等待,由于鼠标使用中断传输方式,
* 不允许睡眠状态,data 又是周期性获取鼠标事件的存储区,因此使用GFP_ATOMIC 优先级,如果不能
* 分配到内存则立即返回0。
*/
mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma);
if (!mouse->data)
goto fail1;
/*
* 为urb 结构体申请内存空间,第一个参数表示等时传输时
需要传送包的数量,其它传输方式则为0。
* 申请的内存将通过下面即将见到的usb_fill_int_urb 函数进行填充。
*/
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!mouse->irq)
goto fail2;
/* 填充usb 设备结构体和输入设备结构体*/
mouse->usbdev = dev;
mouse->dev = input_dev;
/* 获取鼠标设备的名称*/
if (dev->manufacturer)
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
if (dev->product)
{
if (dev->manufacturer)
strlcat(mouse->name, " ", sizeof(mouse->name));
strlcat(mouse->name, dev->product, sizeof(mouse->name));
}
if (!strlen(mouse->name))
snprintf(mouse->name, sizeof(mouse->name),
"USB HIDBP Mouse %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
/*
* 填充鼠标设备结构体中的节点名。usb_make_path 用来获取USB 设备在Sysfs 中的路径,格式
* 为:usb-usb 总线号-路径名。
*/
usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
strlcat(mouse->phys, "/input0", sizeof(mouse->phys));
/* 将鼠标设备的名称赋给鼠标设备内嵌的输入子系统结构体*/
input_dev->name = mouse->name;
/* 将鼠标设备的设备节点名赋给鼠标设备内嵌的输入子系统结构体*/
input_dev->phys = mouse->phys;
/*
* input_dev 中的input_id 结构体,用来存储厂商、设备类型和设备的编号,这个函数是将设备描述符
* 中的编号赋给内嵌的输入子系统结构体
*/
usb_to_input_id(dev, &input_dev->id);
/* cdev 是设备所属类别(class device)*/
input_dev->cdev.dev = &intf->dev;
/* evbit 用来描述事件,EV_KEY 是按键事件,EV_REL 是相对坐标事件*/
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
/* keybit 表示键值,包括左键、右键和中键*/
input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
/* relbit 用于表示相对坐标值*/
input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
/* 有的鼠标还有其它按键*/
input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
/* 中键滚轮的滚动值*/
input_dev->relbit[0] |= BIT(REL_WHEEL);
/* input_dev 的private 数据项用于表示当前输入设备的种类,这里将鼠标结构体对象赋给它*/
input_dev->private = mouse;
/* 填充输入设备打开函数指针*/
input_dev->open = usb_mouse_open;
/* 填充输入设备关闭函数指针*/
input_dev->close = usb_mouse_close;
/*
* 填充构建urb,将刚才填充好的mouse 结构体的数据填充进urb 结构体中,在open 中递交urb。
* 当urb 包含一个即将传输的DMA 缓冲区时应该设置URB_NO_TRANSFER_DMA_MAP。USB核心使用
* transfer_dma变量所指向的缓冲区,而不是transfer_buffer变量所指向的。
* URB_NO_SETUP_DMA_MAP 用于Setup 包,URB_NO_TRANSFER_DMA_MAP 用于所有Data 包。
*/
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
(maxp > 8 ? 8 : maxp),
usb_mouse_irq, mouse, endpoint->bInterval);
mouse->irq->transfer_dma = mouse->data_dma;
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* 向系统注册输入设备*/
input_register_device(mouse->dev);
/*
* 一般在probe 函数中,都需要将设备相关信息保存在一个usb_interface 结构体中,以便以后通过
* usb_get_intfdata 获取使用。这里鼠标设备结构体信息将保存在intf 接口结构体内嵌的设备结构体中
* 的driver_data 数据成员中,即intf->dev->dirver_data = mouse。
*/
usb_set_intfdata(intf, mouse);
return 0;
fail2: usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
fail1: input_free_device(input_dev);
kfree(mouse);
return -ENOMEM;
}
/*
* 鼠标设备拔出时的处理函数
static void usb_mouse_disconnect(struct usb_interface *intf)
{
/* 获取鼠标设备结构体*/
struct usb_mouse *mouse = usb_get_intfdata (intf);
/* intf->dev->dirver_data = NULL,将接口结构体中的鼠标设备指针置空。*/
usb_set_intfdata(intf, NULL);
if (mouse)
{
/* 结束urb 生命周期*/
usb_kill_urb(mouse->irq);
/* 将鼠标设备从输入子系统中注销*/
input_unregister_device(mouse->dev);
/* 释放urb 存储空间*/
usb_free_urb(mouse->irq);
/* 释放存放鼠标事件的data 存储空间*/
usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);
/* 释放存放鼠标结构体的存储空间*/
kfree(mouse);
}
}
/*
* usb_device_id 结构体用于表示该驱动程序所支持的设备,USB_INTERFACE_INFO 可以用来匹配特定类型的接口,
* 这个宏的参数意思为(类别, 子类别, 协议)。
* USB_INTERFACE_CLASS_HID 表示是一种HID (Human Interface Device),即人机交互设备类别;
* USB_INTERFACE_SUBCLASS_BOOT 是子类别,表示是一种boot 阶段使用的HID;
* USB_INTERFACE_PROTOCOL_MOUSE 表示是鼠标设备,遵循鼠标的协议。
*/
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
/*
* 这个宏用来让运行在用户空间的程序知道这个驱动程序能够支持的设备,对于USB 驱动程序来说,第一个参数必须
* 是usb。
*/
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
/*
* 鼠标驱动程序结构体
*/
static struct usb_driver usb_mouse_driver = {
.name = "usbmouse",
.probe = usb_mouse_probe,
.disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
};
/*
* 驱动程序生命周期的开始点,向USB core 注册这个鼠标驱动程序。
*/
static int __init usb_mouse_init(void)
{
int retval = usb_register(&usb_mouse_driver);
if (retval == 0)
info(DRIVER_VERSION ":"
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
实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用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驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。
Linux驱动程序工作原理简介 一、linux驱动程序的数据结构 (1) 二、设备节点如何产生? (2) 三、应用程序是如何访问设备驱动程序的? (2) 四、为什么要有设备文件系统? (3) 五、设备文件系统如何实现? (4) 六、如何使用设备文件系统? (4) 七、具体设备驱动程序分析 (5) 1、驱动程序初始化时,要注册设备节点,创建子设备文件 (5) 2、驱动程序卸载时要注销设备节点,删除设备文件 (7) 参考书目 (8) 一、linux驱动程序的数据结构 设备驱动程序实质上是提供一组供应用程序操作设备的接口函数。 各种设备由于功能不同,驱动程序提供的函数接口也不相同,但linux为了能够统一管理,规定了linux下设备驱动程序必须使用统一的接口函数file_operations 。 所以,一种设备的驱动程序主要内容就是提供这样的一组file_operations 接口函数。 那么,linux是如何管理种类繁多的设备驱动程序呢? linux下设备大体分为块设备和字符设备两类。 内核中用2个全局数组存放这2类驱动程序。 #define MAX_CHRDEV 255 #define MAX_BLKDEV 255 struct device_struct { const char * name; struct file_operations * fops; }; static struct device_struct chrdevs[MAX_CHRDEV]; static struct { const char *name; struct block_device_operations *bdops; } blkdevs[MAX_BLKDEV]; //此处说明一下,struct block_device_operations是块设备驱动程序内部的接口函数,上层文件系统还是通过struct file_operations访问的。
Linux设备驱动程序学习(18)-USB 驱动程序(三) (2009-07-14 11:45) 分类:Linux设备驱动程序 USB urb (USB request block) 内核使用2.6.29.4 USB 设备驱动代码通过urb和所有的 USB 设备通讯。urb用 struct urb 结构描述(include/linux/usb.h )。 urb以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。 一个 urb 的典型生命循环如下: (1)被创建; (2)被分配给一个特定 USB 设备的特定端点; (3)被提交给 USB 核心; (4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动; (5)被 USB 主机控制器驱动处理, 并传送到设备; (6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。 urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB 核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。 struct urb
struct list_head urb_list;/* list head for use by the urb's * current owner */ struct list_head anchor_list;/* the URB may be anchored */ struct usb_anchor *anchor; struct usb_device *dev;/* 指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被USB 驱动初始化.*/ struct usb_host_endpoint *ep;/* (internal) pointer to endpoint */ unsigned int pipe;/* 这个 urb 所要发送到的特定struct usb_device 的端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.必须由下面的函数生成*/ int status;/*当 urb开始由 USB 核心处理或处理结束, 这个变量被设置为 urb 的当前状态. USB 驱动可安全访问这个变量的唯一时间是在 urb 结束处理例程函数中. 这个限制是为防止竞态. 对于等时 urb, 在这个变量中成功值(0)只表示这个 urb 是否已被去链. 为获得等时 urb 的详细状态, 应当检查 iso_frame_desc 变量. */ unsigned int transfer_flags;/* 传输设置*/ void*transfer_buffer;/* 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。对控制端点, 这个缓冲区用于数据中转*/ dma_addr_t transfer_dma;/* 用于以 DMA 方式传送数据到 USB 设备的缓冲区*/ int transfer_buffer_length;/* transfer_buffer 或者 transfer_dma 变量指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用。对于一个 OUT 端点, 如果这个端点大小比这个变量指定的值小, 对这个USB 设备的传输将被分成更小的块,以正确地传送数据。这种大的传送以连续的 USB 帧进行。在一个 urb 中提交一个大块数据, 并且使 USB 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/
---简介 Linux下的MISC简单字符设备驱动虽然使用简单,但却不灵活。 只能建立主设备号为10的设备文件。字符设备比较容易理解,同时也能够满足大多数简 单的硬件设备,字符设备通过文件系统中的名字来读取。这些名字就是文件系统中的特 殊文件或者称为设备文件、文件系统的简单结点,一般位于/dev/目录下使用ls进行查 看会显示以C开头证明这是字符设备文件crw--w---- 1 root tty 4, 0 4月 14 11:05 tty0。 第一个数字是主设备号,第二个数字是次设备号。 ---分配和释放设备编号 1)在建立字符设备驱动时首先要获取设备号,为此目的的必要的函数是 register_chrdev_region,在linux/fs.h中声明:int register_chrdev_region(dev_t first, unsigned int count, char *name);first是你想 要分配的起始设备编号,first的次编号通常是0,count是你请求的连续设备编号的 总数。count如果太大会溢出到下一个主设备号中。name是设备的名字,他会出现在 /proc/devices 和sysfs中。操作成功返回0,如果失败会返回一个负的错误码。 2)如果明确知道设备号可用那么上一个方法可行,否则我们可以使用内核动态分配的设 备号int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);dev是个只输出的参数,firstminor请求的第一个要用的次编号, count和name的作用如上1)对于新驱动,最好的方法是进行动态分配 3)释放设备号,void unregister_chrdev_region(dev_t first unsigned int count); ---文件操作file_operations结构体,内部连接了多个设备具体操作函数。该变量内部 的函数指针指向驱动程序中的具体操作,没有对应动作的指针设置为NULL。 1)fops的第一个成员是struct module *owner 通常都是设置成THIS_MODULE。 linux/module.h中定义的宏。用来在他的操作还在被使用时阻止模块被卸载。 2)loff_t (*llseek) (struct file *, loff_t, int);该方法用以改变文件中的当前读/ 写位置 返回新位置。 3)ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);该函数用 以从设备文件 中读取数据,读取成功返回读取的字节数。
Linux设备驱动程序学习(20)-内存映射和DMA-基本概念 (2011-09-25 15:47) 标签: 虚拟内存设备驱动程序Linux技术分类:Linux设备驱动程序 这部分主要研究 Linux 内存管理的基础知识, 重点在于对设备驱动有用的技术. 因为许多驱动编程需要一些对于虚拟内存(VM)子系统原理的理解。 而这些知识主要分为三个部分: 1、 mmap系统调用的实现原理:它允许设备内存直接映射到一个用户进程地址 空间. 这样做对一些设备来说可显著地提高性能. 2、与mmap的功能相反的应用原理:内核态代码如何跨过边界直接存取用户空间的内存页. 虽然较少驱动需要这个能力. 但是了解如何映射用户空间内存到内 核(使用 get_user_pages)会有用. 3、直接内存存取( DMA ) I/O 操作, 它提供给外设对系统内存的直接存取. 但所有这些技术需要理解 Linux 内存管理的基本原理, 因此我将先学习VM子 系统的基本原理. 一、Linux的内存管理 这里重点是 Linux 内存管理实现的主要特点,而不是描述操作系统的内存管理理论。Linux虚拟内存管理非常的复杂,要写可以写一本书:《深入理解Linux 虚拟内存管理》。学习驱动无须如此深入, 但是对它的工作原理的基本了解是必要的. 解了必要的背景知识后,才可以学习内核管理内存的数据结构. Linux是一个虚拟内存系统(但是在没有MMU的CPU中跑的ucLinux除外), 意味着在内核启动了MMU 之后所有使用的地址不直接对应于硬件使用的物理地址,这些地址(称之为虚拟地址)都经过了MMU转换为物理地址之后再从CPU的内存总线中发出,读取/写入数据. 这样 VM 就引入了一个间接层, 它是许多操作成为可能: 1、系统中运行的程序可以分配远多于物理内存的内存空间,即便单个进程都可拥有一个大于系统的物理内存的虚拟地址空间. 2、虚拟内存也允许程序对进程的地址空间运用多种技巧, 包括映射程序的内存到设备内存.等等~~~ 1、地址类型 Linux 系统处理几种类型的地址, 每个有它自己的含义: 用户虚拟地址:User virtual addresses,用户程序见到的常规地址. 用户地址在长度上是 32 位或者 64 位, 依赖底层的硬件结构, 并且每个进程有它自己 的虚拟地址空间.
1 什么是驱动 a)裸板驱动 b)有系统驱动linux 将驱动封装了一套框架(每个驱动) c)大量和硬件无关的代码已写好只需要编程实现和硬件相关的代码 d)难点:框架的理解代码的理解 e)需要三方面的知识: i.硬件相关的知识 1.电路原理图 2.芯片的数据手册 3.总线协议rs232 i2c等 ii.内核的知识 1.内核驱动属于内核的一部分,它运行在内核态需要对内核知识有了解 2.内存管理 3.解决竞争状态(如上锁) 4.。。。 iii.驱动框架的知识 1.内核中已经实现了大量硬件驱动完成了驱动的框架编程只需要根据硬 件进行添加 2 搭建linux驱动开发工具 a)安装交叉编译环境 i.arm-linux-gcc uboot PATH b)移植uboot c)移植内核 d)制作根文件系统然后通过nfs方式让开发板可以加载 3 内核驱动开发的基本知识 a)如何学驱动编程? i.最好的老师就是内核源码(没有man 功能) 1.要是用某个函数就去查看某个函数的定义注释 2.查看内核中其他模块儿时如何使用该函数的 3.专业书籍: a)内核开发:linux内核的设计与实现机械工程出版社 b)驱动开发:圣经级别的-LDD3:LINUX DEVICE c)操作性别叫强的:精通linux设备驱动程序开发
关于linux内核: 1)linux内核中所使用的函数都是自身实现的它肯定不会调用c库中的函数 2)linux中代码绝大多数代码时gun c语言完成的不是标准c语言可以理解为标c的扩展版和少部分汇编 需要注意的问题: 1)内核态不能做浮点数运算 2)用户空间的每个进程都有独立的0-3G的虚拟空间 多个进程共享同一个内核 内核使用的地址空间为3G-4G 3)每个线程有独立的栈空间 4 写一个最简单的内核模块儿(因为驱动时内核的一个模块套路都一样) a)几个宏 i.__FUNCTION__:展开为所在函数的名称 ii.__LINE__:展开为printk所在的行号 iii.__DATE__:展开为编译程序的日期 b)通用头文件 i.#include
Linux设备驱动程序简介 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel 中的函数,有些常用的操作要自己来编写,而且调试也不方便。本人这几周来为实验室自行研制的一块多媒体卡编制了驱动程序,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。 以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正. 一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作.设备驱动程序是内核的一部分,它完成以下的功能: 1.对设备初始化和释放. 2.把数据从内核传送到硬件和从硬件读取数据. 3.读取应用程序传送给设备文件的数据和回送应用程序请求的数据. 4.检测和处理设备出现的错误. 在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备.字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作.块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待. 已经提到,用户进程是通过设备文件来与实际的硬件打交道.每个设备文件都都有其文件属性(c/b),表示是字符设备还蔤强樯璞?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序. 最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度.也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作.如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck. 读/写时,它首先察看缓冲区的内容,如果缓冲区的数据 如何编写Linux操作系统下的设备驱动程序 二、实例剖析 我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备驱动程序.不过我的kernel是2.0.34,在低版本的kernel上可能会出现问题,我还没测试过. [code]#define __NO_VERSION__
本讲主要概述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 Kernel 系统架构图 一、驱动程序的特点 ?是应用和硬件设备之间的一个软件层。 ?这个软件层一般在内核中实现 ?设备驱动程序的作用在于提供机制,而不是提供策略,编写访问硬件的内核代码时不要给用户强加任何策略 o机制:驱动程序能实现什么功能。 o策略:用户如何使用这些功能。 二、设备驱动分类和内核模块 ?设备驱动类型。Linux 系统将设备驱动分成三种类型 o字符设备 o块设备 o网络设备 ?内核模块:内核模块是内核提供的一种可以动态加载功能单元来扩展内核功能的机制,类似于软件中的插件机制。这种功能单元叫内核模块。 ?通常为每个驱动创建一个不同的模块,而不在一个模块中实现多个设备驱动,从而实现良好的伸缩性和扩展性。 三、字符设备 ?字符设备是个能够象字节流<比如文件)一样访问的设备,由字符设备驱动程序来实现这种特性。通过/dev下的字符设备文件来访问。字符设备驱动程序通常至少需要实现 open、close、read 和 write 等系统调用 所对应的对该硬件进行操作的功能函数。 ?应用程序调用system call<系统调用),例如:read、write,将会导致操作系统执行上层功能组件的代码,这些代码会处理内核的一些内部 事务,为操作硬件做好准备,然后就会调用驱动程序中实现的对硬件进 行物理操作的函数,从而完成对硬件的驱动,然后返回操作系统上层功 能组件的代码,做好内核内部的善后事务,最后返回应用程序。 ?由于应用程序必须使用/dev目录下的设备文件<参见open调用的第1个参数),所以该设备文件必须事先创建。谁创建设备文件呢? ?大多数字符设备是个只能顺序访问的数据通道,不能前后移动访问指针,这点和文件不同。比如串口驱动,只能顺序的读写设备。然而,也 存在和数据区或者文件特性类似的字符设备,访问它们时可前后移动访
如何编写Linux设备驱动程序 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel中的函数,有些常用的操作要自己来编写,而且调试也不方便。 以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料。 一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能: 1。对设备初始化和释放。 2。把数据从内核传送到硬件和从硬件读取数据。 3。读取应用程序传送给设备文件的数据和回送应用程序请求的数据。 4。检测和处理设备出现的错误。 在Linux操作系统下有三类主要的设备文件类型,一是字符设备,二是块设备,三是网络设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他
参考: 韦东山视频第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设备驱动程序学习(10)-时间、延迟及延缓操作 Linux设备驱动程序学习(10) -时间、延迟及延缓操作 度量时间差 时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据HZ 值来设定,HZ 是一个体系依赖的值,在
的.config文件中定义,并没有在make menuconfig的配置选项中出现。Linux的\arch\arm\configs\s3c2410_defconfig文件中的定义为: 所以正常情况下s3c24x0的HZ为200。这一数值在后面的实验中可以证实。 每次发生一个时钟中断,内核内部计数器的值就加一。这个计数器在系统启动时初始化为0,因此它代表本次系统启动以来的时钟嘀哒数。这个计数器是一个64-位变量( 即便在32-位的体系上)并且称为“jiffies_64”。但是驱动通常访问jiffies 变量(unsigned long)(根据体系结构的不同:可能是jiffies_64 ,可能是jiffies_64 的低32位)。使用jiffies 是首选,因为它访问更快,且无需在所有的体系上实现原子地访问64-位的jiffies_64 值。 使用jiffies 计数器 这个计数器和用来读取它的工具函数包含在
怎样写linux下的USB设备驱动程序 发布时间:2007年11月19日 引言 随着人们生活水平的提高,我们用到的USB设备也越来越多,但是Linux在硬件配置上仍然没有做到完全即插即用,对于Linux怎样配置和使用他们,也越来越成为困扰我们的一大问题;本文的目地是使大家了解怎样编制USB设备驱动,为更好地配置和使用USB设备提供方便;对于希望开发Linux系统下USB设备驱动的人员,也可作为进一步学习USB驱动的大体架构进而编写出特殊USB设备的驱动程序。 USB基础知识 USB是英文Universal Serial Bus的缩写,意为通用串行总线。USB最初是为了替代许多不同的低速总线(包括并行、串行和键盘连接)而设计的,它以单一类型的总线连接各种不同的类型的设备。USB的发展已经超越了这些低速的连接方式,它现在可以支持几乎所有可以连接到PC上的设备。最新的USB规范修订了理论上高达480Mbps的高速连接。Linux内核支持两种主要类型的USB驱动程序:宿主系统上的驱动程序和设备上的驱动程序,从宿主的观点来看(一个普通的宿主也就是一个PC机),宿主系统的USB设备驱动程序控制插入其中的USB设备,而USB设备的驱动程序控制该设备如何作为一个USB设备和主机通信。本文将详细介绍运行于PC机上的USB系统是如何运作的。并同时用USB驱动程序的框架程序当例子作详细的说明,我们在此文中不讨论USB器件的驱动程序。 USB驱动程序基础 在动手写USB驱动程序这前,让我们先看看写的USB驱动程序在内核中的结构,如下图: USB驱动程序存在于不同的内核子系统和USB硬件控制器之间,USB核心为USB驱动程序提供了一个用于访问和控制USB硬件的接口,而不必考虑系统当前存在的各种不同类型的USB硬件控制器。USB是一个非常复杂的设备,linux内核为我们提供了一个称为USB的核心的子系统来处理大部分的复杂性,USB
收稿日期:2005-09-22 作者简介:朱华生(1965-),男,江西临川人,副教授. 文章编号:1006-4869(2005)04-0051-03 基于Linux 系统的HHARM9电机驱动程序设计 朱华生,胡凯利 (南昌工程学院计算机科学与技术系,江西南昌330099) 摘 要:对嵌入式Linux 操作系统驱动程序的组成进行分析,讨论了驱动程序的基本框架,以HHARM9电机控制为实例,详细论述了电机驱动程序的实现过程. 关键词:嵌入式;Linux;驱动程序 中图分类号:TP316 文献标识码:A Linux System -Based Design of HHARM 9Electromotor Driver ZHU Hua -sheng,HU Ka-i li (Department of Computer and Science,Nanchang Institute of Technology,Nanchang 330099,China) Abstract:The paper analyses the composition of driver in embedded linux system,disuses its basic frame of driver,and illustrales the process of driver design of HHARM9electromotor in detail. Key words:Embedded;Linux; driver 嵌入式Linux 操作系统因具有免费、开放源代码、强大的网络功能等 特点,在嵌入式产品中得到越来越广泛的应用.基于Linux 操作系统的嵌入 式产品结构[1]如图1所示.本文主要探讨嵌入式系统驱动程序的设计. 1 嵌入式Linux 操作系统驱动程序简介 1)驱动程序和应用程序的区别 驱动程序的设计和应用程序的设计有很大的区别[2].首先,驱动程序 的设计要对硬件的结构、信号的工作流程十分清楚,而在应用程序的设计 中,一般不需要了解这些.其次,应用程序一般有一个main 函数,从头到尾 执行一个任务;驱动程序却不同,它没有main 函数,通过使用宏module _init(初始化函数名),将初始化函数加入内核全局初始化函数列表中,在内核初始化时执行驱动的初始化函数,从而完成驱动的初始化和注册,之后驱动便停止等待被应用软件调用.应用程序可以和GLIB C 库连接,因此可以包含标准的头文件,比如
字符设备驱动程序 实验报告 院系名称: 学生姓名: 学号: 专业名称: 班级: 年月日至时间: 年月日
实验题目字符设备驱动程序 一、实验目的 通过编写一个简单的C语言字符设备驱动程序,来加深对上次的内存管理实验的复习,以及对本次学习的字符设备驱动的应用。 二、实验内容 编写一个字符设备驱动程序,以内核模块的形式插入内核,编译方法与内核编译方法一致。创建设备节点,然后通过编写一个测试程序,输出“hello world!”。 三、实验步骤 ①用C语言编写一个字符设备驱动程序; ②编译,链接,将程序插入内核模块中; ③创建设备节点,编写测试程序,运行输出“hello world”。 四、调试以及运行过程 因为用的是学校的电脑,在运行时,就出现错误。开始是怎么都插不进去模块。最后发现原来是makefile文件开始用小写,后来改成Makefile,,竟然对了。虽然最后显示内核模块插入了,但是无法进行编译,总是显示,无法正常运行。最后,把代码拷进同学电脑里,对了。才发现,有时要相信自己,换一个位置去试,就会发现惊喜。 五、心得体会 对于程序,我们要多练,才能懂得其真正的用处在哪里。没有编写程序运行前只知道一点皮毛,真正操作后才会受益匪浅。通过编写字符设备驱动程序,我知道了当我们不会写代码时,可以先试着把别人的类似代码敲一遍,然后找出那种属于自己的感觉,理解清楚别人的思想,然后根据需要编写属于自己的代码。 六、源代码 1.字符设备驱动程序 #include