Linux-USB驱动(6)-USB下载线驱动设计

在裸机代码中通常需要使用USB来下载u-boot代码。当然下载内核代码使用usb下载也是可以的。接下来我们要完成的就是DNW的USB下载器模块设计。

 

框架

首先完成模块代码的初始化函数和退出函数。

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驱动。

 

接下来实现最简单的id_table,首先我们需要知道USB设备的2个ID,把USB设备插入到PC机中,然后使用lsusb可以查看到所有的USB设备的ID。找到和当前设备对应的即可。

static struct usb_device_id dnw_id_table [] = {
	{ USB_DEVICE(0x5345, 0x1234) },
	{ }		
};

 

probe函数

对于之前我们分析的鼠标驱动程序,它是围绕URB和输入设备来展开。我们这里的下载器,显然也需要URB,但它是一个什么样的设备呢?其实它是一个字符类设备,我么需要在操作系统里面实现读和写的代码。而对于URB的初始化函数,不同的传输需要用不同的初始化函数,我们这里的下载器显然就是一个批量传输的设备了。

 

对于一个批量传输的设备,它的URB创建、初始化、提交可以通过一个函数来完成,就是usb_bulk_msg这个函数。那么这个函数在哪实现了?一般是放在写函数里面实现的,因为作为一个USB下载器来讲,只有当真正要下载东西,写数据的时候,才有必要提交这么一个URB。

 

所以probe函数里面只需要完成字符设备的初始化,对于字符设备的初始化可以使用cdev来实现。对于USB的字符设备,内核还提供了这么一个函数来实现,就是usb_register_dev函数,我们可以在内核里面搜索这个函数学习它的用法。它的原型是这个样子的:

/**
 * usb_register_dev - register a USB device, and ask for a minor number
 * @intf: pointer to the usb_interface that is being registered
 * @class_driver: pointer to the usb_class_driver for this device
 *
 * This should be called by all USB drivers that use the USB major number.
 * If CONFIG_USB_DYNAMIC_MINORS is enabled, the minor number will be
 * dynamically allocated out of the list of available ones.  If it is not
 * enabled, the minor number will be based on the next available free minor,
 * starting at the class_driver->minor_base.
 *
 * This function also creates a usb class device in the sysfs tree.
 *
 * usb_deregister_dev() must be called when the driver is done with
 * the minor numbers given out by this function.
 *
 * Returns -EINVAL if something bad happens with trying to register a
 * device, and 0 on success.
 */
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 struct file_operations dnw_ops =
{
	.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;
}


 

源代码

最后附上驱动代码和应用的测试代码:

#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 struct file_operations dnw_ops =
{
	.owner =	THIS_MODULE,
	.write =	dnw_write,
	.open =		dnw_open,
	.release =	dnw_release,
};

static struct usb_class_driver dnw_class = {
	.name =		"secbulk%d",
	.fops =		&dnw_ops,
	.minor_base =	100,
};


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;
    
    
    //找到并保存usb_bulk_msg需要用到的bulk_out端点
    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));
	
}

static void dnw_disconnect(struct usb_interface *intf)
{
	usb_deregister_dev(intf,&dnw_class);
}

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


编译之后安装这个模块,然后编译应用代码,执行下载操作,查看是否下载成功。

更多Linux资料及视频教程点击这里

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值