导航:首页 > 操作系统 > linux设备驱动程序源码

linux设备驱动程序源码

发布时间:2022-09-22 13:47:24

❶ 在linux编译字符设备驱动程序但无法识别头文件感觉是内核缺少头文件包,如何用apt命令安装内核源代码包

1、你的字符设备驱动是单独一个设备模块吗?
一般情况下头文件是全的,检查一下你的Makefile中kernel目录指定得是否正确:
KDIR := /lib/moles/`uname -r`/build
或者:
KERNEL := /usr/src/linux-source-2.6.32
2、如果要安装全部源码的话先下载下来,再解压到/usr/src
sudo apt-get install linux-source

❷ 如何写linux pci设备驱动程序

Linux下PCI设备驱动开发
1. 关键数据结构
PCI设备上有三种地址空间:PCI的I/O空间、PCI的存储空间和PCI的配置空间。CPU可以访问PCI设备上的所有地址空间,其中I/O空间和存储空间提供给设备驱动程序使用,而配置空间则由Linux内核中的PCI初始化代码使用。内核在启动时负责对所有PCI设备进行初始化,配置好所有的PCI设备,包括中断号以及I/O基址,并在文件/proc/pci中列出所有找到的PCI设备,以及这些设备的参数和属性。
Linux驱动程序通常使用结构(struct)来表示一种设备,而结构体中的变量则代表某一具体设备,该变量存放了与该设备相关的所有信息。好的驱动程序都应该能驱动多个同种设备,每个设备之间用次设备号进行区分,如果采用结构数据来代表所有能由该驱动程序驱动的设备,那么就可以简单地使用数组下标来表示次设备号。
在PCI驱动程序中,下面几个关键数据结构起着非常核心的作用:
pci_driver
这个数据结构在文件include/linux/pci.h里,这是Linux内核版本2.4之后为新型的PCI设备驱动程序所添加的,其中最主要的是用于识别设备的id_table结构,以及用于检测设备的函数probe( )和卸载设备的函数remove( ):
struct pci_driver {
struct list_head node;
char *name;
const struct pci_device_id *id_table;
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id);
void (*remove) (struct pci_dev *dev);
int (*save_state) (struct pci_dev *dev, u32 state);
int (*suspend)(struct pci_dev *dev, u32 state);
int (*resume) (struct pci_dev *dev);
int (*enable_wake) (struct pci_dev *dev, u32 state, int enable);
};
pci_dev
这个数据结构也在文件include/linux/pci.h里,它详细描述了一个PCI设备几乎所有的
硬件信息,包括厂商ID、设备ID、各种资源等:
struct pci_dev {
struct list_head global_list;
struct list_head bus_list;
struct pci_bus *bus;
struct pci_bus *subordinate;
void *sysdata;
struct proc_dir_entry *procent;
unsigned int devfn;
unsigned short vendor;
unsigned short device;
unsigned short subsystem_vendor;
unsigned short subsystem_device;
unsigned int class;
u8 hdr_type;
u8 rom_base_reg;
struct pci_driver *driver;
void *driver_data;
u64 dma_mask;
u32 current_state;
unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];
unsigned int irq;
struct resource resource[DEVICE_COUNT_RESOURCE];
struct resource dma_resource[DEVICE_COUNT_DMA];
struct resource irq_resource[DEVICE_COUNT_IRQ];
char name[80];
char slot_name[8];
int active;
int ro;
unsigned short regs;
int (*prepare)(struct pci_dev *dev);
int (*activate)(struct pci_dev *dev);
int (*deactivate)(struct pci_dev *dev);
};
2. 基本框架
在用模块方式实现PCI设备驱动程序时,通常至少要实现以下几个部分:初始化设备模块、设备打开模块、数据读写和控制模块、中断处理模块、设备释放模块、设备卸载模块。下面给出一个典型的PCI设备驱动程序的基本框架,从中不难体会到这几个关键模块是如何组织起来的。
/* 指明该驱动程序适用于哪一些PCI设备 */
static struct pci_device_id demo_pci_tbl [] __initdata = {
{PCI_VENDOR_ID_DEMO, PCI_DEVICE_ID_DEMO,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEMO},
{0,}
};
/* 对特定PCI设备进行描述的数据结构 */
struct demo_card {
unsigned int magic;
/* 使用链表保存所有同类的PCI设备 */
struct demo_card *next;
/* ... */
}
/* 中断处理模块 */
static void demo_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
/* ... */
}
/* 设备文件操作接口 */
static struct file_operations demo_fops = {
owner: THIS_MODULE, /* demo_fops所属的设备模块 */
read: demo_read, /* 读设备操作*/
write: demo_write, /* 写设备操作*/
ioctl: demo_ioctl, /* 控制设备操作*/
mmap: demo_mmap, /* 内存重映射操作*/
open: demo_open, /* 打开设备操作*/
release: demo_release /* 释放设备操作*/
/* ... */
};
/* 设备模块信息 */
static struct pci_driver demo_pci_driver = {
name: demo_MODULE_NAME, /* 设备模块名称 */
id_table: demo_pci_tbl, /* 能够驱动的设备列表 */
probe: demo_probe, /* 查找并初始化设备 */
remove: demo_remove /* 卸载设备模块 */
/* ... */
};
static int __init demo_init_mole (void)
{
/* ... */
}
static void __exit demo_cleanup_mole (void)
{
pci_unregister_driver(&demo_pci_driver);
}
/* 加载驱动程序模块入口 */
mole_init(demo_init_mole);
/* 卸载驱动程序模块入口 */
mole_exit(demo_cleanup_mole);
上面这段代码给出了一个典型的PCI设备驱动程序的框架,是一种相对固定的模式。需要注意的是,同加载和卸载模块相关的函数或数据结构都要在前面加上__init、__exit等标志符,以使同普通函数区分开来。构造出这样一个框架之后,接下去的工作就是如何完成框架内的各个功能模块了。
3. 初始化设备模块
在Linux系统下,想要完成对一个PCI设备的初始化,需要完成以下工作:
检查PCI总线是否被Linux内核支持;
检查设备是否插在总线插槽上,如果在的话则保存它所占用的插槽的位置等信息。
读出配置头中的信息提供给驱动程序使用。
当Linux内核启动并完成对所有PCI设备进行扫描、登录和分配资源等初始化操作的同时,会建立起系统中所有PCI设备的拓扑结构,此后当PCI驱动程序需要对设备进行初始化时,一般都会调用如下的代码:
static int __init demo_init_mole (void)
{
/* 检查系统是否支持PCI总线 */
if (!pci_present())
return -ENODEV;
/* 注册硬件驱动程序 */
if (!pci_register_driver(&demo_pci_driver)) {
pci_unregister_driver(&demo_pci_driver);
return -ENODEV;
}
/* ... */
return 0;
}
驱动程序首先调用函数pci_present( )检查PCI总线是否已经被Linux内核支持,如果系统支持PCI总线结构,这个函数的返回值为0,如果驱动程序在调用这个函数时得到了一个非0的返回值,那么驱动程序就必须得中止自己的任务了。在2.4以前的内核中,需要手工调用pci_find_device( )函数来查找PCI设备,但在2.4以后更好的办法是调用pci_register_driver( )函数来注册PCI设备的驱动程序,此时需要提供一个pci_driver结构,在该结构中给出的probe探测例程将负责完成对硬件的检测工作。
static int __init demo_probe(struct pci_dev *pci_dev, const struct
pci_device_id *pci_id)
{
struct demo_card *card;
/* 启动PCI设备 */
if (pci_enable_device(pci_dev))
return -EIO;
/* 设备DMA标识 */
if (pci_set_dma_mask(pci_dev, DEMO_DMA_MASK)) {
return -ENODEV;
}
/* 在内核空间中动态申请内存 */
if ((card = kmalloc(sizeof(struct demo_card), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "pci_demo: out of memory\n");
return -ENOMEM;
}
memset(card, 0, sizeof(*card));
/* 读取PCI配置信息 */
card->iobase = pci_resource_start (pci_dev, 1);
card->pci_dev = pci_dev;
card->pci_id = pci_id->device;
card->irq = pci_dev->irq;
card->next = devs;
card->magic = DEMO_CARD_MAGIC;
/* 设置成总线主DMA模式 */
pci_set_master(pci_dev);
/* 申请I/O资源 */
request_region(card->iobase, 64, card_names[pci_id->driver_data]);
return 0;
}
4. 打开设备模块
在这个模块里主要实现申请中断、检查读写模式以及申请对设备的控制权等。在申请控制权的时候,非阻塞方式遇忙返回,否则进程主动接受调度,进入睡眠状态,等待其它进程释放对设备的控制权。
static int demo_open(struct inode *inode, struct file *file)
{
/* 申请中断,注册中断处理程序 */
request_irq(card->irq, &demo_interrupt, SA_SHIRQ,
card_names[pci_id->driver_data], card)) {
/* 检查读写模式 */
if(file->f_mode & FMODE_READ) {
/* ... */
}
if(file->f_mode & FMODE_WRITE) {
/* ... */
}
/* 申请对设备的控制权 */
down(&card->open_sem);
while(card->open_mode & file->f_mode) {
if (file->f_flags & O_NONBLOCK) {
/* NONBLOCK模式,返回-EBUSY */
up(&card->open_sem);
return -EBUSY;
} else {
/* 等待调度,获得控制权 */
card->open_mode |= f_mode & (FMODE_READ | FMODE_WRITE);
up(&card->open_sem);
/* 设备打开计数增1 */
MOD_INC_USE_COUNT;
/* ... */
}
}
}
5. 数据读写和控制信息模块
PCI设备驱动程序可以通过demo_fops 结构中的函数demo_ioctl( ),向应用程序提供对硬件进行控制的接口。例如,通过它可以从I/O寄存器里读取一个数据,并传送到用户空间里:
static int demo_ioctl(struct inode *inode, struct file *file, unsigned int
cmd, unsigned long arg)
{
/* ... */
switch(cmd) {
case DEMO_RDATA:
/* 从I/O端口读取4字节的数据 */
val = inl(card->iobae + 0x10);
/* 将读取的数据传输到用户空间 */
return 0;
}
/* ... */
}
事实上,在demo_fops里还可以实现诸如demo_read( )、demo_mmap( )等操作,Linux内核源码中的driver目录里提供了许多设备驱动程序的源代码,找那里可以找到类似的例子。在对资源的访问方式上,除了有I/O指令以外,还有对外设I/O内存的访问。对这些内存的操作一方面可以通过把I/O内存重新映射后作为普通内存进行操作,另一方面也可以通过总线主DMA(Bus Master DMA)的方式让设备把数据通过DMA传送到系统内存中。
6. 中断处理模块
PC的中断资源比较有限,只有0~15的中断号,因此大部分外部设备都是以共享的形式申请中断号的。当中断发生的时候,中断处理程序首先负责对中断进行识别,然后再做进一步的处理。
static void demo_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct demo_card *card = (struct demo_card *)dev_id;
u32 status;
spin_lock(&card->lock);
/* 识别中断 */
status = inl(card->iobase + GLOB_STA);
if(!(status & INT_MASK))
{
spin_unlock(&card->lock);
return; /* not for us */
}
/* 告诉设备已经收到中断 */
outl(status & INT_MASK, card->iobase + GLOB_STA);
spin_unlock(&card->lock);
/* 其它进一步的处理,如更新DMA缓冲区指针等 */
}
7. 释放设备模块
释放设备模块主要负责释放对设备的控制权,释放占用的内存和中断等,所做的事情正好与打开设备模块相反:
static int demo_release(struct inode *inode, struct file *file)
{
/* ... */
/* 释放对设备的控制权 */
card->open_mode &= (FMODE_READ | FMODE_WRITE);
/* 唤醒其它等待获取控制权的进程 */
wake_up(&card->open_wait);
up(&card->open_sem);
/* 释放中断 */
free_irq(card->irq, card);
/* 设备打开计数增1 */
MOD_DEC_USE_COUNT;
/* ... */
}
8. 卸载设备模块
卸载设备模块与初始化设备模块是相对应的,实现起来相对比较简单,主要是调用函数pci_unregister_driver( )从Linux内核中注销设备驱动程序:
static void __exit demo_cleanup_mole (void)
{
pci_unregister_driver(&demo_pci_driver);
}
小结
PCI总线不仅是目前应用广泛的计算机总线标准,而且是一种兼容性最强、功能最全的计算机总线。而Linux作为一种新的操作系统,其发展前景是无法估量的,同时也为PCI总线与各种新型设备互连成为可能。由于Linux源码开放,因此给连接到PCI总线上的任何设备编写驱动程序变得相对容易。本文介绍如何编译Linux下的PCI驱动程序,针对的内核版本是2.4。

❸ 怎样写linux下的USB设备驱动程序

写一个USB的驱动程序最 基本的要做四件事:驱动程序要支持的设备、注册USB驱动程序、探测和断开、提交和控制urb(USB请求块)
驱动程序支持的设备:有一个结构体struct usb_device_id,这个结构体提供了一列不同类型的该驱动程序支持的USB设备,对于一个只控制一个特定的USB设备的驱动程序来说,struct usb_device_id表被定义为:
/* 驱动程序支持的设备列表 */
static struct usb_device_id skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* 终止入口 */
};
MODULE_DEVICE_TABLE (usb, skel_table);
对 于PC驱动程序,MODULE_DEVICE_TABLE是必需的,而且usb必需为该宏的第一个值,而USB_SKEL_VENDOR_ID和 USB_SKEL_PRODUCT_ID就是这个特殊设备的制造商和产品的ID了,我们在程序中把定义的值改为我们这款USB的,如:
/* 定义制造商和产品的ID号 */
#define USB_SKEL_VENDOR_ID 0x1234
#define USB_SKEL_PRODUCT_ID 0x2345
这两个值可以通过命令lsusb,当然你得先把USB设备先插到主机上了。或者查看厂商的USB设备的手册也能得到,在我机器上运行lsusb是这样的结果:
Bus 004 Device 001: ID 0000:0000
Bus 003 Device 002: ID 1234:2345 Abc Corp.
Bus 002 Device 001: ID 0000:0000
Bus 001 Device 001: ID 0000:0000
得到这两个值后把它定义到程序里就可以了。
注册USB驱动程序:所 有的USB驱动程序都必须创建的结构体是struct usb_driver。这个结构体必须由USB驱动程序来填写,包括许多回调函数和变量,它们向USB核心代码描述USB驱动程序。创建一个有效的 struct usb_driver结构体,只须要初始化五个字段就可以了,在框架程序中是这样的:
static struct usb_driver skel_driver = {
.owner = THIS_MODULE,
.name = "skeleton",
.probe = skel_probe,
.disconnect = skel_disconnect,
.id_table = skel_table,
};
探测和断开:当 一个设备被安装而USB核心认为该驱动程序应该处理时,探测函数被调用,探测函数检查传递给它的设备信息,确定驱动程序是否真的适合该设备。当驱动程序因 为某种原因不应该控制设备时,断开函数被调用,它可以做一些清理工作。探测回调函数中,USB驱动程序初始化任何可能用于控制USB设备的局部结构体,它 还把所需的任何设备相关信息保存到一个局部结构体中,
提交和控制urb:当驱动程序有数据要发送到USB设备时(大多数情况是在驱动程序的写函数中),要分配一个urb来把数据传输给设备:
/* 创建一个urb,并且给它分配一个缓存*/
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
retval = -ENOMEM;
goto error;
}
当urb被成功分配后,还要创建一个DMA缓冲区来以高效的方式发送数据到设备,传递给驱动程序的数据要复制到这块缓冲中去:
buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}

if (_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
goto error;
}
当数据从用户空间正确复制到局部缓冲区后,urb必须在可以被提交给USB核心之前被正确初始化:
/* 初始化urb */
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, count, skel_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
然后urb就可以被提交给USB核心以传输到设备了:
/* 把数据从批量OUT端口发出 */
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) {
err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
goto error;
}
当urb被成功传输到USB设备之后,urb回调函数将被USB核心调用,在我们的例子中,我们初始化urb,使它指向skel_write_bulk_callback函数,以下就是该函数:
static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
{
struct usb_skel *dev;

dev = (struct usb_skel *)urb->context;

if (urb->status &&
!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN)) {
dbg("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status);
}

/* 释放已分配的缓冲区 */
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
}
有时候USB驱动程序只是要发送或者接收一些简单的数据,驱动程序也可以不用urb来进行数据的传输,这是里涉及到两个简单的接口函数:usb_bulk_msg和usb_control_msg ,在这个USB框架程序里读操作就是这样的一个应用:
/* 进行阻塞的批量读以从设备获取数据 */
retval = usb_bulk_msg(dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer,
min(dev->bulk_in_size, count),
&count, HZ*10);

/*如果读成功,复制到用户空间 */
if (!retval) {
if (_to_user(buffer, dev->bulk_in_buffer, count))
retval = -EFAULT;
else
retval = count;
}
usb_bulk_msg接口函数的定义如下:
int usb_bulk_msg(struct usb_device *usb_dev,unsigned int pipe,
void *data,int len,int *actual_length,int timeout);
其参数为:
struct usb_device *usb_dev:指向批量消息所发送的目标USB设备指针。
unsigned int pipe:批量消息所发送目标USB设备的特定端点,此值是调用usb_sndbulkpipe或者usb_rcvbulkpipe来创建的。
void *data:如果是一个OUT端点,它是指向即将发送到设备的数据的指针。如果是IN端点,它是指向从设备读取的数据应该存放的位置的指针。
int len:data参数所指缓冲区的大小。
int *actual_length:指向保存实际传输字节数的位置的指针,至于是传输到设备还是从设备接收取决于端点的方向。
int timeout:以Jiffies为单位的等待的超时时间,如果该值为0,该函数一直等待消息的结束。
如果该接口函数调用成功,返回值为0,否则返回一个负的错误值。
usb_control_msg接口函数定义如下:
int usb_control_msg(struct usb_device *dev,unsigned int pipe,__u8 request,__u8requesttype,__u16 value,__u16 index,void *data,__u16 size,int timeout)
除了允许驱动程序发送和接收USB控制消息之外,usb_control_msg函数的运作和usb_bulk_msg函数类似,其参数和usb_bulk_msg的参数有几个重要区别:
struct usb_device *dev:指向控制消息所发送的目标USB设备的指针。
unsigned int pipe:控制消息所发送的目标USB设备的特定端点,该值是调用usb_sndctrlpipe或usb_rcvctrlpipe来创建的。
__u8 request:控制消息的USB请求值。
__u8 requesttype:控制消息的USB请求类型值。
__u16 value:控制消息的USB消息值。
__u16 index:控制消息的USB消息索引值。
void *data:如果是一个OUT端点,它是指身即将发送到设备的数据的指针。如果是一个IN端点,它是指向从设备读取的数据应该存放的位置的指针。
__u16 size:data参数所指缓冲区的大小。
int timeout:以Jiffies为单位的应该等待的超时时间,如果为0,该函数将一直等待消息结束。
如果该接口函数调用成功,返回传输到设备或者从设备读取的字节数;如果不成功它返回一个负的错误值。
这两个接口函数都不能在一个中断上下文中或者持有自旋锁的情况下调用,同样,该函数也不能被任何其它函数取消,使用时要谨慎。
我们要给未知的USB设备写驱动程序,只需要把这个框架程序稍做修改就可以用了,前面我们已经说过要修改制造商和产品的ID号,把0xfff0这两个值改为未知USB的ID号。
#define USB_SKEL_VENDOR_ID 0xfff0
#define USB_SKEL_PRODUCT_ID 0xfff0
还 有就是在探测函数中把需要探测的接口端点类型写好,在这个框架程序中只探测了批量(USB_ENDPOINT_XFER_BULK)IN和OUT端点,可 以在此处使用掩码(USB_ENDPOINT_XFERTYPE_MASK)让其探测其它的端点类型,驱动程序会对USB设备的每一个接口进行一次探测, 当探测成功后,驱动程序就被绑定到这个接口上。再有就是urb的初始化问题,如果你只写简单的USB驱动,这块不用多加考虑,框架程序里的东西已经够用 了,这里我们简单介绍三个初始化urb的辅助函数:
usb_fill_int_urb :它的函数原型是这样的:
void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,
unsigned int pipe,void *transfer_buff,
int buffer_length,usb_complete_t complete,
void *context,int interval);
这个函数用来正确的初始化即将被发送到USB设备的中断端点的urb。
usb_fill_bulk_urb :它的函数原型是这样的:
void usb_fill_bulk_urb(struct urb *urb,struct usb_device *dev,
unsigned int pipe,void *transfer_buffer,
int buffer_length,usb_complete_t complete)
这个函数是用来正确的初始化批量urb端点的。
usb_fill_control_urb :它的函数原型是这样的:
void usb_fill_control_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,unsigned char *setup_packet,void *transfer_buffer,int buffer_length,usb_complete_t complete,void *context);
这个函数是用来正确初始化控制urb端点的。
还有一个初始化等时urb的,它现在还没有初始化函数,所以它们在被提交到USB核心前,必须在驱动程序中手工地进行初始化,可以参考内核源代码树下的/usr/src/~/drivers/usb/media下的konicawc.c文件。

❹ linux的U盘驱动源码

根据你的描述,估计FUSE不适合你。

参考以下几个文件吧(我的系统是DEBIAN LENNY)

localhost:/home/aaa/program# apt-cache search libusb
libusb-0.1-4 - userspace USB programming library
libusb-1.0-0 - userspace USB programming library
libusb-1.0-0-dev - userspace USB programming library development files
libusb-dev - userspace USB programming library development files

用apt-get source libusb下载过来的内如如下所示:
total 1688
-rw-r--r-- 1 500 500 2661 2005-02-14 acinclude.m4
-rw-r--r-- 1 500 500 244051 2006-03-04 aclocal.m4
drwxrwxrwx 2 500 500 4096 2006-03-04 apidocs
-rw-r--r-- 1 500 500 130 2004-04-22 AUTHORS
-rw-r--r-- 1 500 500 16833 2006-03-04 bsd.c
-rw-r--r-- 1 500 500 189 2004-01-28 ChangeLog
-rwxr-xr-x 1 500 500 3642 2004-03-12 compile
-rwxr-xr-x 1 500 500 42037 2004-04-12 config.guess
-rw-r--r-- 1 500 500 2467 2006-03-04 config.h.in
-rwxr-xr-x 1 500 500 30221 2004-04-12 config.sub
-rwxr-xr-x 1 500 500 746195 2006-03-04 configure
-rw-r--r-- 1 500 500 6777 2006-03-04 configure.in
-rw-r--r-- 1 500 500 26428 2004-01-28 COPYING
-rw-r--r-- 1 500 500 35685 2006-03-04 darwin.c
drwxr-xr-x 3 root root 4096 02-19 18:51 debian
-rwxr-xr-x 1 500 500 14841 2004-03-12 depcomp
-rw-r--r-- 1 500 500 15021 2006-03-04 descriptors.c
drwxrwxrwx 2 500 500 4096 02-19 18:49 doc
-rw-r--r-- 1 500 500 44099 2006-03-04 Doxyfile
-rw-r--r-- 1 500 500 44154 2006-02-07 Doxyfile.in
-rw-r--r-- 1 500 500 759 2004-01-28 error.c
-rw-r--r-- 1 500 500 716 2004-01-28 error.h
-rw-r--r-- 1 500 500 2043 2006-03-04 INSTALL.libusb
-rw-r--r-- 1 500 500 2063 2004-01-28 INSTALL.libusb.in
-rwxr-xr-x 1 500 500 9208 2004-03-12 install-sh
-rw-r--r-- 1 500 500 1267 2004-01-28 libusb-config.in
-rw-r--r-- 1 500 500 196 2005-02-15 libusb.pc.in
-rw-r--r-- 1 500 500 1290 2006-03-04 libusb.spec
-rw-r--r-- 1 500 500 1293 2006-03-04 libusb.spec.in
-rw-r--r-- 1 500 500 2248 2004-01-28 LICENSE
-rw-r--r-- 1 500 500 19148 2006-03-04 linux.c
-rw-r--r-- 1 500 500 3146 2005-02-03 linux.h
-rw-r--r-- 1 500 500 183730 2004-04-12 ltmain.sh
-rw-r--r-- 1 500 500 2220 2006-03-04 Makefile.am
-rw-r--r-- 1 500 500 34139 2006-03-04 Makefile.in
-rwxr-xr-x 1 500 500 10678 2004-03-12 missing
-rw-r--r-- 1 500 500 8 2004-01-28 NEWS
-rw-r--r-- 1 500 500 2546 2006-03-04 README
-rw-r--r-- 1 500 500 2556 2006-03-04 README.in
drwxrwxrwx 2 500 500 4096 2006-03-04 tests
-rw-r--r-- 1 500 500 6443 2006-03-04 usb.c
-rw-r--r-- 1 500 500 8367 2006-03-04 usb.h.in
-rw-r--r-- 1 500 500 1771 2006-03-04 usbi.h
-rw-r--r-- 1 500 500 13511 2006-03-04 usbpp.cpp
-rw-r--r-- 1 500 500 24428 2005-02-10 usbpp.h
可以从usb.c, usb.h.in入手来研究。

❺ linux设备驱动程序 为什么没有源码

找到你要的程序所在文件夹。。直接make....不过,八成会报错。。ldd2太老了

ldd2基于2.4的,很老了。。看ldd3吧,也有代码。。。

❻ 什么是Linux设备驱动程序代码mole

很多情况下,我们期望通过参数来控制我们的驱动的行为,比如由于系统的不同,而为了保证我们驱动有较好的移植性,我们有时候期望通过传递参数来控制我们驱动的行为,这样不同的系统中,驱动可能有不同的行为控制。

为了满足这种需求,内核允许对驱动程序指定参数,而这些参数可在加载驱动的过程中动态的改变

参数的来源主要有两个

❼ linux设备驱动程序示例代码在哪

在内核源码的drivers目录

❽ 如何编写Linux 驱动程序

如何编写Linux设备驱动程序
回想学习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),表示是字符设备还是块设备?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们。设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。
最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。
读/写时,它首先察看缓冲区的内容,如果缓冲区的数据未被处理,则先处理其中的内容。
如何编写Linux操作系统下的设备驱动程序

二、实例剖析
我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理。把下面的C代码输入机器,你就会获得一个真正的设备驱动程序。
#define __NO_VERSION__
#include <linux/moles.h>
#include <linux/version.h>
char kernel_version [] = UTS_RELEASE;
这一段定义了一些版本信息,虽然用处不是很大,但也必不可少。Johnsonm说所有的驱动程序的开头都要包含<linux/config.h>,一般来讲最好使用。
由于用户进程是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如 open,read,write,close…, 注意,不是fopen, fread,但是如何把系统调用和驱动程序关联起来呢?这需要了解一个非常关键的数据结构:
struct file_operations
{
int (*seek) (struct inode * ,struct file *, off_t ,int);
int (*read) (struct inode * ,struct file *, char ,int);
int (*write) (struct inode * ,struct file *, off_t ,int);
int (*readdir) (struct inode * ,struct file *, struct dirent * ,int);
int (*select) (struct inode * ,struct file *, int ,select_table *);
int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long);
int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);
int (*open) (struct inode * ,struct file *);
int (*release) (struct inode * ,struct file *);
int (*fsync) (struct inode * ,struct file *);
int (*fasync) (struct inode * ,struct file *,int);
int (*check_media_change) (struct inode * ,struct file *);
int (*revalidate) (dev_t dev);
}

这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。
下面就开始写子程序。
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include<linux/config.h>
#include <linux/errno.h>
#include <asm/segment.h>
unsigned int test_major = 0;
static int read_test(struct inode *node,struct file *file,char *buf,int count)
{
int left;
if (verify_area(VERIFY_WRITE,buf,count) == -EFAULT )
return -EFAULT;
for(left = count ; left > 0 ; left--)
{
__put_user(1,buf,1);
buf++;
}
return count;
}

这个函数是为read调用准备的。当调用read时,read_test()被调用,它把用户的缓冲区全部写1。buf 是read调用的一个参数。它是用户进程空间的一个地址。但是在read_test被调用时,系统进入核心态。所以不能使用buf这个地址,必须用__put_user(),这是kernel提供的一个函数,用于向用户传送数据。另外还有很多类似功能的函数。请参考Robert着的《Linux内核设计与实现》(第二版)。然而,在向用户空间拷贝数据之前,必须验证buf是否可用。这就用到函数verify_area。
static int write_tibet(struct inode *inode,struct file *file,const char *buf,int count)
{
return count;
}
static int open_tibet(struct inode *inode,struct file *file )
{
MOD_INC_USE_COUNT;
return 0;
}
static void release_tibet(struct inode *inode,struct file *file )
{
MOD_DEC_USE_COUNT;
}

这几个函数都是空操作。实际调用发生时什么也不做,他们仅仅为下面的结构提供函数指针。
struct file_operations test_fops = {
NULL,
read_test,
write_test,
NULL, /* test_readdir */
NULL,
NULL, /* test_ioctl */
NULL, /* test_mmap */
open_test,
release_test,
NULL, /* test_fsync */
NULL, /* test_fasync */
/* nothing more, fill with NULLs */
};
这样,设备驱动程序的主体可以说是写好了。现在要把驱动程序嵌入内核。驱动程序可以按照两种方式编译。一种是编译进kernel,另一种是编译成模块(moles),如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐使用模块方式。
int init_mole(void)
{
int result;
result = register_chrdev(0, "test", &test_fops);
if (result < 0) {
printk(KERN_INFO "test: can't get major number\n");
return result;
}
if (test_major == 0) test_major = result; /* dynamic */
return 0;
}

在用insmod命令将编译好的模块调入内存时,init_mole 函数被调用。在这里,init_mole只做了一件事,就是向系统的字符设备表登记了一个字符设备。register_chrdev需要三个参数,参数一是希望获得的设备号,如果是零的话,系统将选择一个没有被占用的设备号返回。参数二是设备文件名,参数三用来登记驱动程序实际执行操作的函数的指针。
如果登记成功,返回设备的主设备号,不成功,返回一个负值。
void cleanup_mole(void)
{
unregister_chrdev(test_major,"test");
}
在用rmmod卸载模块时,cleanup_mole函数被调用,它释放字符设备test在系统字符设备表中占有的表项。
一个极其简单的字符设备可以说写好了,文件名就叫test.c吧。
下面编译 :
$ gcc -O2 -DMODULE -D__KERNEL__ -c test.c
得到文件test.o就是一个设备驱动程序。
如果设备驱动程序有多个文件,把每个文件按上面的命令行编译,然后
ld -r file1.o file2.o -o molename。
驱动程序已经编译好了,现在把它安装到系统中去。
$ insmod –f test.o
如果安装成功,在/proc/devices文件中就可以看到设备test,并可以看到它的主设备号。要卸载的话,运行 :
$ rmmod test
下一步要创建设备文件。
mknod /dev/test c major minor
c 是指字符设备,major是主设备号,就是在/proc/devices里看到的。
用shell命令
$ cat /proc/devices
就可以获得主设备号,可以把上面的命令行加入你的shell script中去。
minor是从设备号,设置成0就可以了。
我们现在可以通过设备文件来访问我们的驱动程序。写一个小小的测试程序。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main()
{
int testdev;
int i;
char buf[10];
testdev = open("/dev/test",O_RDWR);
if ( testdev == -1 )
{
printf("Cann't open file \n");
exit(0);
}
read(testdev,buf,10);
for (i = 0; i < 10;i++)
printf("%d\n",buf[i]);
close(testdev);
}

编译运行,看看是不是打印出全1 ?
以上只是一个简单的演示。真正实用的驱动程序要复杂的多,要处理如中断,DMA,I/O port等问题。这些才是真正的难点。请看下节,实际情况的处理。
如何编写Linux操作系统下的设备驱动程序
三、设备驱动程序中的一些具体问题
1。 I/O Port。
和硬件打交道离不开I/O Port,老的ISA设备经常是占用实际的I/O端口,在linux下,操作系统没有对I/O口屏蔽,也就是说,任何驱动程序都可对任意的I/O口操作,这样就很容易引起混乱。每个驱动程序应该自己避免误用端口。
有两个重要的kernel函数可以保证驱动程序做到这一点。
1)check_region(int io_port, int off_set)
这个函数察看系统的I/O表,看是否有别的驱动程序占用某一段I/O口。
参数1:I/O端口的基地址,
参数2:I/O端口占用的范围。
返回值:0 没有占用, 非0,已经被占用。
2)request_region(int io_port, int off_set,char *devname)
如果这段I/O端口没有被占用,在我们的驱动程序中就可以使用它。在使用之前,必须向系统登记,以防止被其他程序占用。登记后,在/proc/ioports文件中可以看到你登记的I/O口。
参数1:io端口的基地址。
参数2:io端口占用的范围。
参数3:使用这段io地址的设备名。
在对I/O口登记后,就可以放心地用inb(), outb()之类的函来访问了。
在一些pci设备中,I/O端口被映射到一段内存中去,要访问这些端口就相当于访问一段内存。经常性的,我们要获得一块内存的物理地址。

2。内存操作
在设备驱动程序中动态开辟内存,不是用malloc,而是kmalloc,或者用get_free_pages直接申请页。释放内存用的是kfree,或free_pages。 请注意,kmalloc等函数返回的是物理地址!
注意,kmalloc最大只能开辟128k-16,16个字节是被页描述符结构占用了。
内存映射的I/O口,寄存器或者是硬件设备的RAM(如显存)一般占用F0000000以上的地址空间。在驱动程序中不能直接访问,要通过kernel函数vremap获得重新映射以后的地址。
另外,很多硬件需要一块比较大的连续内存用作DMA传送。这块程序需要一直驻留在内存,不能被交换到文件中去。但是kmalloc最多只能开辟128k的内存。
这可以通过牺牲一些系统内存的方法来解决。

3。中断处理
同处理I/O端口一样,要使用一个中断,必须先向系统登记。
int request_irq(unsigned int irq ,void(*handle)(int,void *,struct pt_regs *),
unsigned int long flags, const char *device);
irq: 是要申请的中断。
handle:中断处理函数指针。
flags:SA_INTERRUPT 请求一个快速中断,0 正常中断。
device:设备名。

如果登记成功,返回0,这时在/proc/interrupts文件中可以看你请求的中断。
4。一些常见的问题。
对硬件操作,有时时序很重要(关于时序的具体问题就要参考具体的设备芯片手册啦!比如网卡芯片RTL8139)。但是如果用C语言写一些低级的硬件操作的话,gcc往往会对你的程序进行优化,这样时序会发生错误。如果用汇编写呢,gcc同样会对汇编代码进行优化,除非用volatile关键字修饰。最保险的办法是禁止优化。这当然只能对一部分你自己编写的代码。如果对所有的代码都不优化,你会发现驱动程序根本无法装载。这是因为在编译驱动程序时要用到gcc的一些扩展特性,而这些扩展特性必须在加了优化选项之后才能体现出来。
写在后面:学习Linux确实不是一件容易的事情,因为要付出很多精力,也必须具备很好的C语言基础;但是,学习Linux也是一件非常有趣的事情,它里面包含了许多高手的智慧和“幽默”,这些都需要自己亲自动手才能体会到,O(∩_∩)O~哈哈!

❾ 如何编写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设备驱动程序中的源程序sbull时,出现以下问题要怎么解决啊

可能是你的内核版本和源码不匹配。那本书是针对2.6.10的。

阅读全文

与linux设备驱动程序源码相关的资料

热点内容
ai文件pdf 浏览:907
腾讯云服务器挂载混合云 浏览:758
智能小车用什么单片机 浏览:463
java怎么给窗口关闭 浏览:940
列举51单片机的寻址方式 浏览:706
剪辑app怎么写长篇文字 浏览:400
app专属流量过月租怎么不更新 浏览:654
王者程序员都有谁 浏览:76
给牛换脚掌解压 浏览:387
围棋有多少种算法 浏览:602
unity资源包在哪个文件夹 浏览:704
阿里云服务器远程链接不成功 浏览:482
文件系统pdf 浏览:766
原神安卓区服什么意思 浏览:37
贝壳app怎么线上发布 浏览:159
如何挑选安卓系统机顶盒 浏览:54
安卓快充使用有什么注意事项 浏览:909
黑马程序员的云计算网课 浏览:947
endnotestyle文件夹怎么导入 浏览:460
讲解少儿编程演讲会开头 浏览:426