1.模块代码的初始化函数和退出函数
static struct usb_driver dnw_driver =
{
.name = "dnw", /* 驱动名 */
.probe = dnw_probe, /* 捕获函数 */
.disconnect = dnw_disconnect, /* 卸载函数 */
.id_table = dnw_id_table, /* 设备列表 */
};
int dnw_init()
{
usb_register(&dnw_driver);
return 0;
}
void dnw_exit()
{
usb_deregister(&dnw_driver);
}
module_init(dnw_init);
module_exit(dnw_exit);
MODULE_LICENSE("GPL");
- 初始化函数里面注册一个USB驱动,退出函数里面注销一个USB驱动。
2.实现id_table
- 接下来实现最简单的id_table,首先我们需要知道USB设备的2个ID,把USB设备插入到PC机中,然后使用lsusb可以查看到所有的USB设备的ID。找到和当前设备对应的即可。
static struct usb_device_id dnw_id_table [] =
{
{ USB_DEVICE(0x5345, 0x1234) },
{ }
};
3.probe函数
- 对于之前我们分析的鼠标驱动程序,它是围绕URB和输入设备来展开。我们这里的下载器,显然也需要URB,但它是一个什么样的设备呢?其实它是一个字符类设备,我么需要在操作系统里面实现读和写的代码。而对于URB的初始化函数,不同的传输需要用不同的初始化函数,我们这里的下载器显然就是一个批量传输的设备了。
- 对于一个批量传输的设备,它的URB创建、初始化、提交可以通过一个函数来完成,就是usb_bulk_msg这个函数。那么这个函数在哪实现了?一般是放在写函数里面实现的,因为作为一个USB下载器来讲,只有当真正要下载东西,写数据的时候,才有必要提交这么一个URB。
- 所以probe函数里面只需要完成字符设备的初始化,对于字符设备的初始化可以使用cdev来实现。对于USB的字符设备,内核还提供了这么一个函数来实现,就是usb_register_dev函数,我们可以在内核里面搜索这个函数学习它的用法。它的原型是这个样子的:
int usb_register_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
- 它的第一个参数是USB设备的接口,我们可以通过probe的参数来拿到,第二个参数是class_driver,它里面包含了字符设备的名字,函数操作集等等:
static struct usb_class_driver dnw_class =
{
.name = "dnw%d", // 设备文件名字
.fops = &dnw_ops, // 函数操作集
.minor_base = 100, // 次设备号,字符设备的主设备号为固定的180
};
- 接下来实现dnw_ops,即操作函数集,因为它不需要读,因此实现写即可。
static const struct file_operations dnw_fops =
{
.owner = THIS_MODULE,
.write = dnw_write,
.open = dnw_open,
.release = dnw_release,
};
- 这些打开,写操作函数我们可能不知道它的函数原型,怎么办呢?当然是从Linux代码中找呀。
- 先来构思dnw_write函数,之前提到的usb_bulk_msg函数,不仅实现URB操作,它还能把用户的数据写入USB设备中。
#define BULKOUT_BUFFER_SIZE 512
char *bulkout_buffer;
static int dnw_open(struct inode* inode, struct file *file)
{
// 为这个buffer分配空间,大小为512个字节
bulkout_buffer = kmalloc(BULKOUT_BUFFER_SIZE,GFP_KERNEL);
return 0;
}
static int dnw_release (struct inode* inode, struct file *file)
{
// 这里面释放buffer的空间
kfree(bulkout_buffer);
return 0;
}
static ssize_t dnw_write(struct file *file, const char __user *buf, size_t len, loff_t *pos)
{
size_t to_write;
size_t total_write = 0;
size_t act_len;
while(len>0)
{
// 计算这次需要拷贝的数据量
to_write = min(len,(size_t)BULKOUT_BUFFER_SIZE);
/* 把用户空间的数据分批拷贝到内核中
* buffer的内存分配可以在open函数中完成
* buffer的释放在release中完成
*/
copy_from_user(bulkout_buffer,buf+total_write,to_write);
// 提交URB
usb_bulk_msg(udev,usb_sndbulkpipe(udev,bulk_out_endaddr),bulkout_buffer,to_write,&act_len,3*HZ);
// 更新数据量
len -= to_write;
total_write += to_write;
}
return total_write;
}
4.源代码
- 驱动代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#define BULKOUT_BUFFER_SIZE 512
char *bulkout_buffer;
struct usb_device *udev;
__u8 bulk_out_endaddr;
static struct usb_device_id dnw_id_table [] =
{
{ USB_DEVICE(0x5345, 0x1234) },
{ }
};
static int dnw_open(struct inode* inode, struct file *file)
{
// 为这个buffer分配空间,大小为512个字节
bulkout_buffer = kmalloc(BULKOUT_BUFFER_SIZE, GFP_KERNEL);
return 0;
}
static int dnw_release(struct inode* inode, struct file *file)
{
// 这里面释放buffer的空间
kfree(bulkout_buffer);
return 0;
}
static ssize_t dnw_write(struct file *file, const char __user *buf, size_t len, loff_t *pos)
{
size_t to_write;
size_t total_write = 0; // 已经发送的数据量
size_t act_len;
while (len > 0)
{
// 计算这次需要拷贝的数据量
to_write = min(len, (size_t)BULKOUT_BUFFER_SIZE);
/* 把用户空间的数据分批拷贝到内核中
* buffer的内存分配可以在open函数中完成
* buffer的释放在release中完成
*/
copy_from_user(bulkout_buffer, buf + total_write, to_write);
// 提交URB
usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_endaddr), bulkout_buffer, to_write, &act_len, 3 * HZ);
// 更新数据量
len -= to_write;
total_write += to_write;
}
return total_write;
}
static const struct file_operations dnw_fops =
{
.owner = THIS_MODULE,
.write = dnw_write,
.open = dnw_open,
.release = dnw_release,
};
static struct usb_class_driver dnw_class =
{
.name = "dnw%d", // 设备文件名字
.fops = &dnw_ops, // 函数操作集
.minor_base = 100, // 次设备号,字符设备的主设备号为固定的180
};
static int dnw_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
/* 接口设置描述 */
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int i;
// 获取当前的接口设置
interface = intf->cur_altsetting;
for (i = 0; i < interface->desc.bNumEndpoints; i++)
{
endpoint = &interface->endpoint[i].desc;
if (usb_endpoint_is_bulk_out(endpoint))
{
bulk_out_endaddr = endpoint->bEndpointAddress;
break;
}
}
usb_register_dev(intf, &dnw_class);
udev = usb_get_dev(interface_to_usbdev(intf));
return 0;
}
static void dnw_disconnect(struct usb_interface *intf)
{
usb_deregister_dev(intf,&dnw_class);
}
static struct usb_driver dnw_driver =
{
.name = "dnw", /* 驱动名 */
.probe = dnw_probe, /* 捕获函数 */
.disconnect = dnw_disconnect, /* 卸载函数 */
.id_table = dnw_id_table, /* 设备列表 */
};
int dnw_init()
{
usb_register(&dnw_driver);
return 0;
}
void dnw_exit()
{
usb_deregister(&dnw_driver);
}
module_init(dnw_init);
module_exit(dnw_exit);
MODULE_LICENSE("GPL");
- 测试代码
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
const char* dev = "/dev/dnw0";
int main(int argc, char* argv[])
{
unsigned char* file_buffer = NULL;
long int addr = 0;
if( 3 != argc )
{
printf("Usage: dwn <filename> address\n");
return 1;
}
int fd = open(argv[1], O_RDONLY);
if(-1 == fd)
{
printf("Can not open file - %s\n", argv[1]);
return 1;
}
addr = strtol((char *) argv[2] ,NULL, 16);
printf("addr = %x \n", addr);
// get file size
struct stat file_stat;
if( -1 == fstat(fd, &file_stat) )
{
printf("Get file size filed!\n");
return 1;
}
file_buffer = (unsigned char*)malloc(file_stat.st_size+10);
if(NULL == file_buffer)
{
printf("malloc failed!\n");
goto error;
}
//memset(file_buffer, '\0', sizeof(file_buffer)); // bad code ! corrected by Qulory
memset(file_buffer, '\0', sizeof(char)*(file_stat.st_size+10));
// the first 8 bytes in the file_buffer is reserved, the last 2 bytes also;
if( file_stat.st_size != read(fd, file_buffer+8, file_stat.st_size))
{
printf("Read file failed!\n");
goto error;
}
printf("File name : %s\n", argv[1]);
printf("File size : %ld bytes\n", file_stat.st_size);// off_t is long int
int fd_dev = open(dev, O_WRONLY);
if( -1 == fd_dev) {
printf("Can not open %s\n", dev);
goto error;
}
/*
* Note: the first 4 bytes store the dest addr ;
* the following 4 bytes store the file size ;
* and the last 2 bytes store the sum of each bytes of the file ;
*/
*((unsigned long*)file_buffer) = addr; //load address
*((unsigned long*)file_buffer+1) = file_stat.st_size+10; //file size
unsigned short sum = 0;
int i;
for(i=8; i<file_stat.st_size+8; i++)
{
sum += file_buffer[i];
}
*((unsigned short*)(file_buffer+8+file_stat.st_size)) = sum;
printf("Start Sending data...\n");
size_t remain_size = file_stat.st_size+10;
size_t block_size = 512;
size_t written = 0;
while(remain_size > 0)
{
size_t to_write = remain_size > block_size ? block_size:remain_size;
size_t real_write = write(fd_dev, file_buffer+written, to_write);
if( to_write != real_write)
{
printf(" write /dev/secbulk0 failed! to_write = %u real_write = %u \n" , to_write ,real_write );
return 1;
}
remain_size -= to_write;
written += to_write;
printf("\rSent %lu%% \t %u bytes !", written*100/(file_stat.st_size+10), written);
fflush(stdout);
}
printf("OK\n");
return 0;
error:
if(-1 != fd_dev)
{
close(fd_dev);
}
if(fd != -1)
{
close(fd);
}
if( NULL != file_buffer )
{
free(file_buffer);
}
return -1;
}
- Makefile
KDIR = /lib/modules/`uname -r`/build
PWD := $(shell pwd)
obj-m := dnw_usb.o
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *.ko *.ko.unsigned *.mod.c *.order *.symvers
- 编译之后安装这个模块,然后编译应用代码,执行下载操作,查看是否下载成功。