驱动程序设计
使用Linux命令mknod建立一个设备文件。比如输入mknod /dev/my_disk b 42 0命令,说明要建立一个块设备文件,名字是my_disk,主设备号是42,次设备号是0。
看看你是否成功的建立了这个文件。
输入命令ls -l /dev/my_disk
从我这里copy两个文件radimo.c和radimo.h
不妨放在/root目录下
• 编译这个程序:
• gcc -c -I/usr/src/linux-2.4.20-8/include radimo.c
• 编译生成一个模块radimo.o,就是我们的驱动程序。
• 加载这个模块:insmod radimo.o
• 刚才我们已经创建了一个设备文件/dev/my_disk,在驱动程序没有加载之前,这个设备文件是无效的。加载驱动程序后,这个设备才有效。这是一个用内存虚拟的磁盘,容量2M。
• 格式化这个磁盘,使用ext2格式:
mke2fs /dev/my_disk
• 在系统中挂接这个磁盘:
mount /dev/my_disk /mnt
• 查看系统中已经挂接的磁盘:
输入命令df,至少可以看到两个磁盘,一个是硬盘,一个是刚才挂接的虚拟磁盘。
• 玩够了,就输入umount /dev/my_disk卸载这个虚拟磁盘。
• 最后输入rmmod radimo卸载这个驱动程序。
• 我们来一起分析radimo.c这个程序,看看驱动程序的工作原理。
• 主要包含以下几个函数:
– radimo_request
– radimo_open
– radimo_ioctl
– radimo_release
– init_module
– cleanup_module
–
init_module的流程
• 函数的调用时机:模块加载时由内核调用。
1.申请内存,注意要用内核函数vmalloc,而不是C库函数malloc
2.向系统注册块设备,用内核函数register_blkdev(设备号,设备名,函数表)
3.设定设备的读写函数,用内核函数blk_queue_make_request(设备号,读写函数)
4.初始化其他要用的全局变量。
5.在这个程序里面调用MSG宏相当于printk
radimo_open的流程
• 函数的调用时机:当系统准备使用你所负责的设备时,由内核调用。
• 所做的唯一的事情:用MOD_INC_USE_COUNT增加引用计数,也就是说,从这个时候开始,驱动程序不能被随便卸载。
• 还有一些关于定时器的语句,本程序中忽略。
radimo_ioctl的流程
• 用户程序在使用设备之前,会查询设备的属性,比如磁盘大小、速度等等。
• 这些属性由radimo_ioctl函数提供。
• 参数cmd表示要查询设备的哪个属性。我们只关心BLKGETSIZE,就是磁盘的大小。
• 用put_user函数把一个数字写到用户地址空间。
radimo_request的流程
• 将在系统需要读写设备的时候被调用。
• 1.计算要读写的地址。sbh->b_rsector表示要读写的扇区号,radimo_hard表示每个扇区的字节数。那么两者相乘就可以得到要读写的地址(以字节计算)。
• 2.判断访问是否越界?越界的访问是不合法的
• 3.判断是读还是写?
– 如果是读,就把数据从驱动程序的缓冲区中搬到操作系统的缓冲区中。
– 如果是写,就把数据从系统缓冲区中搬到驱动程序的缓冲区中。
其他函数的流程
• radimo_release函数:
– 在关闭设备的时候被调用
– 减少引用计数,允许卸载本模块。
• cleanup_module函数:
– 在模块卸载的时候被调用。
做一些清理现场的工作
你需要做的工作?
• 仔细阅读radimo驱动程序的源代码。
• 不懂的地方,可以参考我编写的实验教案。
• 把这个程序修改成一个U盘的驱动程序。
/*
* My UDisk driver - 0.1
*
* TODO:
* -fix urb->status race condition in write sequence
* - moveminor_table to a dynamic list.
*
* History:
*
* 2008_01_9 - 0.1 - zero out dev in probefunction for devices that do
*
*/
#define__KERNEL__
#define MODULE
#include<linux/config.h>
#include<linux/kernel.h>
#include<linux/sched.h>
#include <linux/signal.h>
#include<linux/errno.h>
#include<linux/poll.h>
#include<linux/init.h>
#include<linux/slab.h>
#include<linux/fcntl.h>
#include<linux/module.h>
#include<linux/spinlock.h>
#include<linux/list.h>
#include<linux/smp_lock.h>
#include <linux/devfs_fs_kernel.h>
#include<linux/usb.h>
#define MAJOR_NR 42 //定义主设备号
#defineDEVICE_NAME "myudisk" // 定义设备名
#defineDEVICE_NR(device) (MINOR(device))
#defineDEVICE_ON(device)
#defineDEVICE_OFF(device)
#defineDEVICE_NO_RANDOM
#include <linux/blk.h>
#defineMYUDISK_SECTOR_BITS 9 /* 2**9 byte hardware sector */
#defineMYUDISK_BLOCK_SIZE 1024 /* block size */ //定义了一个块的大小,以字节为单位
#defineMYUDISK_TOTAL_SIZE 2048 /* size in blocks */ //定义了这个虚拟盘的容量,以块为单位
/* the storagepool */
//static char*myudisk_storage; //这个指针是全局变量,指向用于虚拟盘的内存
//static intmyudisk_sectorsize = 1 << MYUDISK_SECTOR_BITS;
//static intmyudisk_blocksize = MYUDISK_BLOCK_SIZE;
static intmyudisk_size ;
static intmyudisk_readahead = 4;
#defineCONFIG_USB_DEBUG
#ifdefCONFIG_USB_DEBUG
static int debug = 1;
#else
static int debug;
#endif
/* Use our owndbg macro */
#undef dbg
#definedbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ "(%s):" format "\n" , __FUNCTION__, ## arg); } while (0)
/* VersionInformation */
#defineDRIVER_VERSION "v0.1"
#defineDRIVER_AUTHOR "Wen Yan Jun, yjwen@nudt.edu.cn"
#defineDRIVER_DESC "My UDisk Driver"
/* Moduleparamaters */
MODULE_PARM(debug,"i");
MODULE_PARM_DESC(debug,"Debug enabled or not");
/* Define thesevalues to match your device */
#defineMY_UDISK_VENDOR_ID 0x058f
#defineMY_UDISK_PRODUCT_ID 0x6387
/* table ofdevices that work with this driver */
static structusb_device_id myudisk_table [] = {
{ USB_DEVICE(MY_UDISK_VENDOR_ID,MY_UDISK_PRODUCT_ID) },
{ } /*Terminating entry */
};
MODULE_DEVICE_TABLE(usb, myudisk_table);
/* Get a minorrange for your devices from the usb maintainer */
#defineMY_UDISK_MINOR_BASE 192
/* we can have upto this number of device plugged in at once */
#defineMAX_DEVICES 16
/* Structure tohold all of our device specific stuff */
struct my_udisk {
struct usb_device * udev; /*save off the usb device pointer */
struct usb_interface * interface; /*the interface for this device */
devfs_handle_t devfs; /* devfs device node */
unsigned char minor; /* the starting minor numberfor this device */
unsigned char num_ports; /* the number of ports this device has*/
char num_interrupt_in; /* number of interrupt in endpoints we have*/
char num_bulk_in; /* number of bulk in endpoints wehave */
char num_bulk_out; /* number of bulk out endpoints wehave */
unsigned char * bulk_in_buffer; /*the buffer to receive data */
int bulk_in_size; /* the size of the receive buffer*/
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
unsigned char * bulk_out_buffer; /* thebuffer to send data */
int bulk_out_size; /* the size of the send buffer */
struct urb * write_urb; /* the urb used to send data */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
struct tq_struct tqueue; /*task queue for line discipline waking up */
int open_count; /* number of times this port has beenopened */
struct semaphore sem; /*locks this structure */
};
/* the global usbdevfs handle */
externdevfs_handle_t usb_devfs_handle;
//
#defineMAX_COMMAND_SIZE 16
#defineMU_BULK_BUFFER_LEN 4096 /* if the blocksize is larger than thebuffer len, data transform will fail. */
/* transportreturn codes */
#defineMYUDISK_TRANSPORT_GOOD 0 /*Transport good, command good */
#defineMYUDISK_TRANSPORT_FAILED 1 /* Transport good, command failed */
#defineMYUDISK_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */
#defineMYUDISK_TRANSPORT_ABORTED 3 /*Transport aborted */
#defineSCSI_DATA_UNKNOWN 0
#defineSCSI_DATA_WRITE 1
#defineSCSI_DATA_READ 2
#defineSCSI_DATA_NONE 3
static inlinevoid myudisk_debug_data (const char *function, int size, const unsigned char*data)
{
int i;
if (!debug)
return;
printk (KERN_DEBUG __FILE__" [%s]: length= %d, data = << ",
function, size);
for (i = 0; i < size; ++i) {
printk ("%.2x ", data[i]);
}
//printk (">>\n");
}
structcmnd_struct {
unsigned char cmnd[MAX_COMMAND_SIZE];
unsigned char cmd_len;
unsigned char sc_data_direction; /* direction of data transfer */
unsigned data_payload_size; /* data payload size */
unsigned data_payload_actualSize;/*data payload size actually xfered */
__u32 tag; /* used for cbw */
};
#defineMYUDISK_TIMEOUT HZ*10
#defineMYUDISK_STRING_LEN 32
#defineSUCCESS 0x2002
#defineFAILED 0x2003
/* command blockwrapper */
structbulk_cb_wrap {
__u32 Signature; /* contains 'USBC' */
__u32 Tag; /* unique per command id */
__u32 DataTransferLength; /* size of data */
__u8 Flags; /* direction in bit 0 */
__u8 Lun; /* LUN normally 0 */
__u8 Length; /* of of the CDB */
__u8 CDB[16]; /* max command */
};
#defineMU_BULK_CB_WRAP_LEN 31
#defineMU_BULK_CB_SIGN 0x43425355 /*spells out USBC */
#defineMU_BULK_FLAG_IN 1
#defineMU_BULK_FLAG_OUT 0
/* bulk-onlyclass specific requests */
#defineMU_BULK_RESET_REQUEST 0xff
#defineMU_BULK_GET_MAX_LUN 0xfe
/* command statuswrapper */
structbulk_cs_wrap {
__u32 Signature; /* should = 'USBS' */
__u32 Tag; /* same as original command*/
__u32 Residue; /* amount not transferred */
__u8 Status; /* see below */
__u8 Filler[18];
};
#defineMU_BULK_CS_WRAP_LEN 13
#defineMU_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */
/* This is forOlympus Camedia digital cameras */
#defineMU_BULK_CS_OLYMPUS_SIGN 0x55425355 /* spells out 'USBU' */
#defineMU_BULK_STAT_OK 0
#defineMU_BULK_STAT_FAIL 1
#defineMU_BULK_STAT_PHASE 2
/* bulk-onlyclass specific requests */
#defineMU_BULK_RESET_REQUEST 0xff
#defineMU_BULK_GET_MAX_LUN 0xfe
__u32myudisk_soft ,SECTOR_num,BLOCK_num;
unsigned longPARTION_offset;
static intmyudisk_hard = 1 << MYUDISK_SECTOR_BITS;
/***********************************************************************
* USB mass storage device related functions
***********************************************************************/
/
/* local functionprototypes */
static intmyudisk_ioctl (struct inode*inode, struct file *file, unsigned int cmd, unsigned long arg);
static intmyudisk_open (struct inode*inode, struct file *file);
static intmyudisk_release (struct inode*inode, struct file *file);
static void *myudisk_probe (struct usb_device*dev, unsigned int ifnum, const struct usb_device_id *id);
static voidmyudisk_disconnect (struct usb_device*dev, void *ptr);
static voidmyudisk_write_bulk_callback (structurb *urb);
extern intmyudisk_Bulk_transport(struct usb_device *udev, struct usb_interface*interface,
structcmnd_struct *cs,
unsigned char*bulk_buffer, int bulk_size,
__u8bulk_in_endpointAddr, __u8 bulk_out_endpointAddr);
unsigned intmyudisk_transfer_length(struct cmnd_struct *ud);
intmyudisk_Bulk_reset(struct usb_device *udev, struct usb_interface *interface,
__u8bulk_in_endpointAddr, __u8 bulk_out_endpointAddr);
/* array ofpointers to our devices that are currently connected */
static structmy_udisk *minor_table[MAX_DEVICES];
/* lock toprotect the minor_table structure */
staticDECLARE_MUTEX (minor_table_mutex);
/*
* File operations needed when we register thisdriver.
* This assumes that this driver NEEDS fileoperations,
* of course, which means that the driver isexpected
* to have a node in the /dev directory. If theUSB
* device were for a network interface then thedriver
* would use "struct net_driver"instead, and a serial
* device would use "structtty_driver".
*/
static struct block_device_operationsmyudisk_fops = {
ioctl: myudisk_ioctl,
open: myudisk_open,
release: myudisk_release,
};
/* usb specificobject needed to register this driver with the usb subsystem */
static structusb_driver myudisk_driver = {
name: "myudisk",
probe: myudisk_probe,
disconnect: myudisk_disconnect,
fops: NULL,
minor: MY_UDISK_MINOR_BASE,
id_table: myudisk_table,
};
/**
* my_udisk_debug_data
*/
static inlinevoid my_udisk_debug_data (const char *function, int size, const unsigned char*data)
{
int i;
if (!debug)
return;
printk (KERN_DEBUG __FILE__": %s - length= %d, data = ",
function, size);
for (i = 0; i < size; ++i) {
printk ("%.2x ", data[i]);
}
//printk ("\n");
}
/**
* myudisk_delete
*/
static inlinevoid myudisk_delete (struct my_udisk *dev)
{
minor_table[dev->minor] = NULL;
if (dev->bulk_in_buffer != NULL)
kfree (dev->bulk_in_buffer);
if (dev->bulk_out_buffer != NULL)
kfree (dev->bulk_out_buffer);
if (dev->write_urb != NULL)
usb_free_urb (dev->write_urb);
kfree (dev);
}
/**
* myudisk_open
*/
static intmyudisk_open (struct inode *inode, struct file *file)
{
struct my_udisk *dev = NULL;
int subminor;
int retval = 0;
dbg("myudisk_open()");
subminor = MINOR (inode->i_rdev); //- MY_UDISK_MINOR_BASE;
if ((subminor < 0) ||
(subminor >= MAX_DEVICES)) {
return -ENODEV;
}
//MOD_INC_USE_COUNT;
/* lock our minor table and get our local datafor this minor */
down (&minor_table_mutex);
dev = minor_table[subminor];
dbg("minor: %d, dev:%p", subminor,dev);
if (dev == NULL) {
up (&minor_table_mutex);
// MOD_DEC_USE_COUNT;
return -ENODEV;
}
/* lock this device */
down (&dev->sem);
/* unlock the minor table */
up (&minor_table_mutex);
/* increment our usage count for the driver */
//++dev->open_count;
/* save our object in the file's privatestructure */
file->private_data = dev;
dbg("file: %p, dev:%p", file, dev);
/* unlock this device */
up (&dev->sem);
dbg("The dev is not NULL\n");
return retval;
}
/**
* myudisk_release
*/
static intmyudisk_release (struct inode *inode, struct file *file)
{
struct my_udisk *dev;
int subminor;
int retval = 0;
dbg("inode: %p, file: %p", inode,file);
subminor = MINOR (inode->i_rdev); //- MY_UDISK_MINOR_BASE;
if ((subminor < 0) ||
(subminor >= MAX_DEVICES)) {
return -ENODEV;
}
/* lock our minor table */
down (&minor_table_mutex);
dev = minor_table[subminor];
if (dev == NULL) {
up (&minor_table_mutex);
dbg("object is NULL");
return -ENODEV;
}
dbg("subminor= %d, dev=%p, dev->minor=%d", subminor, dev, dev->minor);
/* lock our device */
down (&dev->sem);
if (0) { //dev->open_count <= 0) {
dbg("device not opened");
retval = -ENODEV;
goto exit_not_opened;
}
if (dev->udev == NULL) {
/* the device was unplugged before thefile was released */
up (&dev->sem);
myudisk_delete (dev);
up (&minor_table_mutex);
// MOD_DEC_USE_COUNT;
return 0;
}
/* decrement our usage count for the device */
// --dev->open_count;
if (dev->open_count <= 0) {
/* shutdown any bulk writes that mightbe going on */
usb_unlink_urb (dev->write_urb);
dev->open_count = 0;
}
/* decrement our usage count for the module */
// MOD_DEC_USE_COUNT;
exit_not_opened:
up (&dev->sem);
up (&minor_table_mutex);
return retval;
}
/**
* myudisk_ioctl
*/
static intmyudisk_ioctl (struct inode *inode, struct file *file, unsigned int cmd,unsigned long arg)
{
unsigned int minor;
dbg("enter myudisk_ioctl()");
if (!inode || !inode->i_rdev)
return -EINVAL;
minor = MINOR(inode->i_rdev);
switch (cmd) {
case BLKFLSBUF: { //将缓冲写回存储区的操作
/* flush buffers */
dbg("ioctl:BLKFLSBUF\n");
/* deny all but root */
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
break;
}
case BLKGETSIZE: { //得到设备容量的操作
/* return device size */
dbg("ioctl:BLKGETSIZE\n");
dbg("ioctl: BLKGETSIZE =%u\n",myudisk_size);
if (!arg)
return -EINVAL;
return put_user(SECTOR_num,(long *) arg);
}
case BLKRASET: { //设置设备预读值的操作
/* set read ahead value */
int tmp;
dbg("ioctl:BLKRASET\n");
if (get_user(tmp, (long *)arg))
return -EINVAL;
if (tmp > 0xff)
return -EINVAL;
read_ahead[MAJOR_NR] = tmp;
return 0;
}
case BLKRAGET: { //得到设备预读值的操作
/* return read ahead value */
dbg("ioctl:BLKRAGET\n");
if (!arg)
return -EINVAL;
returnput_user(read_ahead[MAJOR_NR], (long *)arg);
}
case BLKSSZGET: { //得到设备块大小的操作
/* return block size */
dbg("ioctl:BLKSSZGET\n");
if (!arg)
return -EINVAL;
return put_user(myudisk_hard,(long *)arg);
}
default: { //其他操作
dbg("ioctl wanted%u\n", cmd);
return -ENOTTY;
}
}
dbg(" exit myudisk_ioctl()");
return 0;
struct my_udisk *dev;
dev = (struct my_udisk*)file->private_data;
/* lock this object */
down (&dev->sem);
/* verify that the device wasn't unplugged */
if (dev->udev == NULL) {
up (&dev->sem);
return -ENODEV;
}
dbg(" - minor %d, cmd 0x%.4x, arg%ld",
dev->minor, cmd, arg);
/* fill in your device specific stuff here */
/* unlock the device */
up (&dev->sem);
/* return that we did not understand thisioctl call */
return -ENOTTY;
}
/**
* myudisk_write_bulk_callback
*/
static voidmyudisk_write_bulk_callback (struct urb *urb)
{
struct my_udisk *dev = (struct my_udisk*)urb->context;
dbg(" - minor %d", dev->minor);
if ((urb->status != -ENOENT) &&
(urb->status != -ECONNRESET)) {
dbg(" - nonzero write bulk statusreceived: %d",
urb->status);
return;
}
return;
}
static intmyudisk_request(request_queue_t *request_queue, int rw, struct buffer_head*sbh)
{
unsigned long total;
//int *make_oops = NULL; // CR: access to this pointer will causeOops, for debug only.
//INIT_REQUEST;
//devno = DEVICE_NR(CURRENT->rq_dev);
/*if(devno>MAX_DEVICES);
{
printk("The device number is toolarage!!!\n");
return 0;
}*/
//struct my_udisk *dev = CURRENT->rq_dev;
struct my_udisk *dev = minor_table[0];
int retval;
if(dev == NULL)
{
printk("Point to a unknowndevice !!! the dev is NULL\n");
return 0;
}
if (dev->udev == NULL) {
printk("the dev->udev isNULL!!!\n");
return -ENODEV;
}
dbg("%s sector rsector = %lu, blocknr =%lu\n",
rw == READ ?"read" : "write",
sbh->b_rsector,
sbh->b_blocknr);
//offset = sbh->b_rsector *myudisk_sectorsize;
total = (unsigned long)sbh->b_size;//计算需要访问的地址和大小
__u32 nLUN = (__u32)(PARTION_offset + sbh->b_rsector);
/* access beyond end of the device */
if (total+nLUN> myudisk_size) {
dbg("Error: access beyond end ofthe device");
/* error in request */
buffer_IO_error(sbh);
return 0;
} //判断访问地址是否越界
dbg("offset = %lu, total = %lu\n",nLUN, total);
//printk("The request has to the readwrite function!!!\n");//singal of using for the debug!
struct cmnd_struct *com;
com = kmalloc (sizeof(structcmnd_struct),GFP_KERNEL);
//**********************/
if (rw == READ)
{ //如果是读操作,从虚拟盘的内存中复制数据到缓冲区中
//memcpy(bh_kmap(sbh),myudisk_storage+offset, total);
dbg("To the Readrequest!!!\n");
memset(dev->bulk_in_buffer,0,dev->bulk_in_size);
com->cmnd[0] = 0x28;
com->cmnd[1] = 0;
com->cmnd[2] = ((unsignedchar)nLUN>>24) & 0xff;
com->cmnd[3] = ((unsignedchar)nLUN>>16) & 0xff;
com->cmnd[4] = ((unsignedchar)nLUN>>8) & 0xff;
com->cmnd[5] = ((unsignedchar)nLUN) & 0xff;
com->cmnd[7] = (unsignedchar)((total/myudisk_soft)>>8) & 0xff;
com->cmnd[8] = (unsignedchar)(total/myudisk_soft) & 0xff;
com->cmd_len = 12;
com->data_payload_size =(unsigned long)sbh->b_size;
com->sc_data_direction =SCSI_DATA_READ;
com->tag = 0x12345678;;
myudisk_Bulk_transport(dev->udev,dev->interface,com, dev->bulk_in_buffer, dev->bulk_in_size,dev->bulk_in_endpointAddr, dev->bulk_out_endpointAddr);
memcpy(bh_kmap(sbh),dev->bulk_in_buffer,sbh->b_size);
memset(dev->bulk_in_buffer,0,dev->bulk_in_size);
dbg("usb_bulk_msg hassuccessfully!!\n");
retval = (unsignedlong)sbh->b_size;
}
else if (rw == WRITE) //如果是写操作,从缓冲区中复制数据到虚拟盘的内存中
{
memset(dev->bulk_out_buffer,0,dev->bulk_out_size);
memcpy(dev->bulk_out_buffer,bh_kmap(sbh), total);
com->cmnd[0] = 0x2A;
com->cmnd[1] = 0;
com->data_payload_size = total;
com->cmnd[2] = ((unsignedchar)nLUN>>24) & 0xff;
com->cmnd[3] = ((unsignedchar)nLUN>>16) & 0xff;
com->cmnd[4] = ((unsigned char)nLUN>>8)& 0xff;
com->cmnd[5] = ((unsigned char)nLUN)& 0xff;
com->cmnd[7] = (unsignedchar)((total/myudisk_soft)>>8) & 0xff;
com->cmnd[8] = (unsignedchar)(total/myudisk_soft) & 0xff;
com->tag = 0x12345678;
com->cmd_len = 12;
com->sc_data_direction =SCSI_DATA_WRITE;
myudisk_Bulk_transport(dev->udev,dev->interface,com, dev->bulk_out_buffer, dev->bulk_out_size,dev->bulk_in_endpointAddr, dev->bulk_out_endpointAddr);
memset(dev->bulk_out_buffer,0,dev->bulk_out_size);
}
else { /*can't happen */
dbg("cmd == %d is invalid\n",rw);
}
/* successful */
sbh->b_end_io(sbh,1); //结束读写操作
kfree(com);
return 0;
}
/**
* myudisk_probe
*
* Calledby the usb core when a new device is connected that it thinks
* thisdriver might be interested in.
*/
static void *myudisk_probe(struct usb_device *udev, unsigned int ifnum, const structusb_device_id *id)
{
struct my_udisk *dev = NULL;
struct usb_interface *interface;
struct usb_interface_descriptor *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int minor;
int buffer_size;
int i;
char name[10];
dbg("myudisk_probe()");
printk("The core has to here!!!!\n");
/* See if the device offered us matches whatwe can accept */
if ((udev->descriptor.idVendor != MY_UDISK_VENDOR_ID)||
(udev->descriptor.idProduct != MY_UDISK_PRODUCT_ID)) {
return NULL;
}
printk("drivers has successfully detectedthe device !!\n");
/* select a "subminor" number (partof a minor number) */
down (&minor_table_mutex);
for (minor = 0; minor < MAX_DEVICES;++minor) {
if (minor_table[minor] == NULL)
break;
}
if (minor >= MAX_DEVICES) {
info ("Too many devices pluggedin, can not handle this device.\n");
goto exit;
}
/* allocate memory for our device state and intializeit */
dev = kmalloc (sizeof(struct my_udisk),GFP_KERNEL);
if (dev == NULL) {
err ("Out of memory");
goto exit;
}
memset (dev, 0x00, sizeof (*dev));
minor_table[minor] = dev;
printk("The subminor is %d The device is %p\n",minor,minor_table[minor]);
interface =&udev->actconfig->interface[ifnum];
init_MUTEX (&dev->sem);
dev->udev = udev;
dev->interface = interface;
dev->minor = minor;
/* set up the endpoint information */
/* check out the endpoints */
iface_desc = &interface->altsetting[0];
for (i = 0; i <iface_desc->bNumEndpoints; ++i) {
endpoint =&iface_desc->endpoint[i];
if ((endpoint->bEndpointAddress& 0x80) &&
((endpoint->bmAttributes & 3) == 0x02)) {
/* we found a bulk in endpoint*/
buffer_size= 4096;
dev->bulk_in_size =buffer_size;
dev->bulk_in_endpointAddr =endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!dev->bulk_in_buffer) {
err("Couldn'tallocate bulk_in_buffer");
gotoerror;
}
}
if (((endpoint->bEndpointAddress& 0x80) == 0x00) &&
((endpoint->bmAttributes & 3) == 0x02)) {
/* we found a bulk out endpoint*/
dev->write_urb =usb_alloc_urb(0);
if (!dev->write_urb) {
err("No free urbs available");
goto error;
}
buffer_size = 4096;
dev->bulk_out_size =buffer_size;
dev->bulk_out_endpointAddr =endpoint->bEndpointAddress;
dev->bulk_out_buffer =kmalloc (buffer_size, GFP_KERNEL);
if (!dev->bulk_out_buffer) {
err("Couldn'tallocate bulk_out_buffer");
goto error;
}
FILL_BULK_URB(dev->write_urb,udev,
usb_sndbulkpipe(udev,
endpoint->bEndpointAddress),
dev->bulk_out_buffer, buffer_size,
myudisk_write_bulk_callback, dev);
}
}
/* initialize the devfs node for this deviceand register it */
sprintf(name, "myudisk%d",dev->minor);
dev->devfs = devfs_register(usb_devfs_handle, name,
DEVFS_FL_DEFAULT, USB_MAJOR,
MY_UDISK_MINOR_BASE + dev->minor,
S_IFCHR | S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP | S_IROTH,
&myudisk_fops, NULL);
///Thispart is to read the flush's capicity and MBR information
structcmnd_struct *com;
com = kmalloc(sizeof(struct cmnd_struct),GFP_KERNEL);
if(com == NULL)
{
dbg("The memory on the com is unavailable!");
goto error;
}
memset(dev->bulk_in_buffer,0,dev->bulk_in_size);
com->cmnd[0] =0x25;
com->tag = 0;
com ->sc_data_direction= SCSI_DATA_READ ;
com->cmnd[1] =0;
com->cmd_len =12;
myudisk_Bulk_transport(dev->udev,dev->interface, com, dev->bulk_in_buffer, dev->bulk_in_size,dev->bulk_in_endpointAddr, dev->bulk_out_endpointAddr);//This is to sendthe command and receive data from device
printk("Tothe after the read capity!");
if(dev == NULL)
{
printk("The dev is NULL!\n");
goto error;
}
BLOCK_num = (((dev->bulk_in_buffer[0] )<< 24) |((dev->bulk_in_buffer[1])<< 16) | ((dev->bulk_in_buffer[2])<< 8) | (dev->bulk_in_buffer[3]) ) + 1;//(__u32*)buffer; block number LBN(n-1)
myudisk_soft = (((dev->bulk_in_buffer[4]) << 24) | ((dev->bulk_in_buffer[5])<< 16) | ((dev->bulk_in_buffer[6]) << 8) |(dev->bulk_in_buffer[7]) );//(__u32*)buffer+4;block size.
myudisk_size= BLOCK_num * myudisk_soft;//device'scapicity by bites
SECTOR_num =(myudisk_size/myudisk_hard);//device's sector number
printk("Thesize is %u,The block size is %u,the num is%u",myudisk_size,myudisk_soft,BLOCK_num);
memset(dev->bulk_in_buffer,0,dev->bulk_in_size);
kfree(com);
//***************************************************///thisis to read MBR information but still have some problem
__u32 nrBlock = 0;//read from the zero sector
unsigned long size = 512;//the read block sizethe first 512 bites is the MBR information
struct cmnd_struct *cs;
cs = kmalloc (sizeof(structcmnd_struct),GFP_KERNEL);
if(cs == NULL)
{
dbg("The memory on the cs isunavailable !");
goto error;
}
cs->cmnd[0] = 0x28;
cs->cmnd[1] =0;
cs->cmnd[2] = ((unsignedchar)nrBlock>>24) & 0xff;
cs->cmnd[3] = ((unsignedchar)nrBlock>>16) & 0xff;
cs->cmnd[4] = ((unsignedchar)nrBlock>>8) & 0xff;
cs->cmnd[5] = ((unsigned char)nrBlock)& 0xff;
cs->cmnd[7] = (unsigned char)((size/myudisk_soft)>>8)& 0xff;
cs->cmnd[8] = (unsignedchar)(size/myudisk_soft) & 0xff;
cs ->tag = 0x12345678;
cs->cmd_len = 12;
cs->sc_data_direction = SCSI_DATA_READ;
cs->data_payload_size = 512;
myudisk_Bulk_transport(dev->udev,dev->interface, cs, dev->bulk_in_buffer, dev->bulk_in_size,dev->bulk_in_endpointAddr, dev->bulk_out_endpointAddr);
__u32 startSector,sectorsNum;
startSector = 0;
sectorsNum = 0;
for(i = 3; i>=1; i--)//computer the startersector and the offset of the device
{
startSector += dev->bulk_in_buffer[0x01c6+ i];
sectorsNum += dev->bulk_in_buffer[0x01ca + i];
startSector <<= 8;
sectorsNum <<= 8;
}
startSector += dev->bulk_in_buffer[0x01c6];
sectorsNum += dev->bulk_in_buffer[0x01ca];
dbg("The disk started at sector%X.\n",startSector);
dbg("The disk has %Xsectors.\n",sectorsNum);
PARTION_offset = startSector;
dbg("The disk offset with %Xsectors.",PARTION_offset);
printk("Next is the disk MBR and DPTinformation.\n");
for(i = 0; i < 512; i++){
printk("%X ",dev->bulk_in_buffer[i]);
if(i ==445)the first 445 bites is the prebootinformation the last is the partationinformation
printk("\nNow is the partitiontable.\n");
if(i%8 == 0)
printk("\n");
}
kfree(cs);
//
/* let the user know what node this device isnow attached to */
info ("My UDisk device now attached tomyudisk%d", dev->minor);
goto exit;
error:
myudisk_delete (dev);
dev = NULL;
exit:
up (&minor_table_mutex);
return dev;
}
/**
* myudisk_disconnect
*
* Calledby the usb core when the device is removed from the system.
*/
/***********************************************************************
* Helper routines
***********************************************************************/
/* Calculate thelength of the data transfer (not the command) for any
* given SCSI command (adapted fromusb__stor_transfer_length())
*/
unsigned intmyudisk_transfer_length(struct cmnd_struct *cs)
{
int doDefault = 0;
unsigned int len = 0;
/* This table tells us:
X =command not supported
L =return length in cmnd[4] (8 bits).
M =return length in cmnd[8] (8 bits).
G =return length in cmnd[3] and cmnd[4] (16 bits)
H =return length in cmnd[7] and cmnd[8] (16 bits)
I =return length in cmnd[8] and cmnd[9] (16 bits)
C =return length in cmnd[2] to cmnd[5] (32 bits)
D =return length in cmnd[6] to cmnd[9] (32 bits)
B =return length in blocksize so we use buff_len
R =return length in cmnd[2] to cmnd[4] (24 bits)
S =return length in cmnd[3] to cmnd[5] (24 bits)
T =return length in cmnd[6] to cmnd[8] (24 bits)
U =return length in cmnd[7] to cmnd[9] (24 bits)
0-9 =fixed return length
V =20 bytes
W =24 bytes
Z =return length is mode dependant or not in command, use buff_len
*/
static char *lengths =
/*0123456789ABCDEF 0123456789ABCDEF */
"00XLZ6XZBXBBXXXB""00LBBLG0R0L0GG0X" /* 00-1F */
"XXXXT8XXB4B0BBBB""ZZZ0B00HCSSZTBHH" /* 20-3F */
"M0HHB0X000H0HH0X""XHH0HHXX0TH0H0XX" /* 40-5F */
"XXXXXXXXXXXXXXXX""XXXXXXXXXXXXXXXX" /* 60-7F */
"XXXXXXXXXXXXXXXX""XXXXXXXXXXXXXXXX" /* 80-9F */
"X0XXX00XB0BXBXBB""ZZZ0XUIDU000XHBX" /* A0-BF */
"XXXXXXXXXXXXXXXX""XXXXXXXXXXXXXXXX" /* C0-DF */
"XDXXXXXXXXXXXXXX""XXW00HXXXXXXXXXX"; /* E0-FF */
/* Commands checked in table:
CHANGE_DEFINITION 40
COMPARE 39
COPY18
COPY_AND_VERIFY 3a
ERASE19
ERASE_10 2c
ERASE_12 ac
EXCHANGE_MEDIUM a6
FORMAT_UNIT 04
GET_DATA_BUFFER_STATUS 34
GET_MESSAGE_10 28
GET_MESSAGE_12 a8
GET_WINDOW 25 !!! Has more datathan READ_CAPACITY, need to fix table
INITIALIZE_ELEMENT_STATUS 07 !!! REASSIGN_BLOCKS luckily uses buff_len
INQUIRY 12
LOAD_UNLOAD 1b
LOCATE 2b
LOCK_UNLOCK_CACHE 36
LOG_SELECT 4c
LOG_SENSE 4d
MEDIUM_SCAN 38 !!! This was M
MODE_SELECT6 15
MODE_SELECT_10 55
MODE_SENSE_6 1a
MODE_SENSE_10 5a
MOVE_MEDIUM a5
OBJECT_POSITION 31 !!! Same asSEARCH_DATA_EQUAL
PAUSE_RESUME 4b
PLAY_AUDIO_10 45
PLAY_AUDIO_12 a5
PLAY_AUDIO_MSF 47
PLAY_AUDIO_TRACK_INDEX 48
PLAY_AUDIO_TRACK_RELATIVE_10 49
PLAY_AUDIO_TRACK_RELATIVE_12 a9
POSITION_TO_ELEMENT 2b
PRE-FETCH 34
PREVENT_ALLOW_MEDIUM_REMOVAL 1e
PRINT0a !!! Same as WRITE_6 but isalways in bytes
READ_6 08
READ_10 28
READ_12 a8
READ_BLOCK_LIMITS 05
READ_BUFFER 3c
READ_CAPACITY 25
READ_CDROM_CAPACITY 25
READ_DEFECT_DATA 37
READ_DEFECT_DATA_12 b7
READ_ELEMENT_STATUS b8 !!! Think this is inbytes
READ_GENERATION 29 !!! Could also be M?
READ_HEADER 44 !!! This was L
READ_LONG 3e
READ_POSITION 34 !!! This shouldbe V but conflicts with PRE-FETCH
READ_REVERSE 0f
READ_SUB-CHANNEL 42 !!! Is this in bytes?
READ_TOC 43 !!! Is this inbytes?
READ_UPDATED_BLOCK 2d
REASSIGN_BLOCKS 07
RECEIVE 08 !!! Same asREAD_6 probably in bytes though
RECEIVE_DIAGNOSTIC_RESULTS 1c
RECOVER_BUFFERED_DATA 14 !!! For PRINTERs this is bytes
RELEASE_UNIT 17
REQUEST_SENSE 03
REQUEST_VOLUME_ELEMENT_ADDRESS b5 !!! Think this is in bytes
RESERVE_UNIT 16
REWIND 01
REZERO_UNIT 01
SCAN1b !!! Conflicts with variouscommands, should be L
SEARCH_DATA_EQUAL 31
SEARCH_DATA_EQUAL_12 b1
SEARCH_DATA_LOW 30
SEARCH_DATA_LOW_12 b0
SEARCH_DATA_HIGH 32
SEARCH_DATA_HIGH_12 b2
SEEK_6 0b !!! Conflictswith SLEW_AND_PRINT
SEEK_10 2b
SEND0a !!! Same as WRITE_6,probably in bytes though
SEND2a !!! Similar to WRITE_10 butfor scanners
SEND_DIAGNOSTIC 1d
SEND_MESSAGE_6 0a !!! Same asWRITE_6 - is in bytes
SEND_MESSAGE_102a !!! Same as WRITE_10 - is in bytes
SEND_MESSAGE_12 aa !!! Same asWRITE_12 - is in bytes
SEND_OPC 54
SEND_VOLUME_TAG b6 !!! Think this is in bytes
SET_LIMITS 33
SET_LIMITS_12 b3
SET_WINDOW 24
SLEW_AND_PRINT 0b !!! Conflicts with SEEK_6
SPACE11
START_STOP_UNIT 1b
STOP_PRINT 1b
SYNCHRONIZE_BUFFER 10
SYNCHRONIZE_CACHE 35
TEST_UNIT_READY 00
UPDATE_BLOCK 3d
VERIFY 13
VERIFY 2f
VERIFY_12 af
WRITE_6 0a
WRITE_10 2a
WRITE_12 aa
WRITE_AND_VERIFY 2e
WRITE_AND_VERIFY_12 ae
WRITE_BUFFER 3b
WRITE_FILEMARKS 10
WRITE_LONG 3f
WRITE_SAME 41
*/
if (cs->sc_data_direction ==SCSI_DATA_WRITE) {
doDefault = 1;
}
else
switch (lengths[cs->cmnd[0]]) {
case 'L':
len = cs->cmnd[4];
break;
case 'M':
len = cs->cmnd[8];
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
len = lengths[cs->cmnd[0]]-'0';
break;
case 'G':
len = (((unsignedint)cs->cmnd[3])<<8) |
cs->cmnd[4];
break;
case 'H':
len = (((unsignedint)cs->cmnd[7])<<8) |
cs->cmnd[8];
break;
case 'I':
len = (((unsignedint)cs->cmnd[8])<<8) |
cs->cmnd[9];
break;
case 'R':
len = (((unsignedint)cs->cmnd[2])<<16) |
(((unsignedint)cs->cmnd[3])<<8) |
cs->cmnd[4];
break;
case 'S':
len = (((unsignedint)cs->cmnd[3])<<16) |
(((unsignedint)cs->cmnd[4])<<8) |
cs->cmnd[5];
break;
case 'T':
len = (((unsignedint)cs->cmnd[6])<<16) |
(((unsignedint)cs->cmnd[7])<<8) |
cs->cmnd[8];
break;
case 'U':
len = (((unsignedint)cs->cmnd[7])<<16) |
(((unsignedint)cs->cmnd[8])<<8) |
cs->cmnd[9];
break;
case 'C':
len = (((unsignedint)cs->cmnd[2])<<24) |
(((unsignedint)cs->cmnd[3])<<16) |
(((unsignedint)cs->cmnd[4])<<8) |
cs->cmnd[5];
break;
case 'D':
len = (((unsignedint)cs->cmnd[6])<<24) |
(((unsignedint)cs->cmnd[7])<<16) |
(((unsignedint)cs->cmnd[8])<<8) |
cs->cmnd[9];
break;
case 'V':
len = 20;
break;
case 'W':
len = 24;
break;
case 'B':
/* Use buffer size due todifferent block sizes */
doDefault = 1;
break;
case 'X':
dbg("Error:UNSUPPORTED COMMAND X\n",
cs->cmnd[0]);
doDefault = 1;
break;
case 'Z':
/* Use buffer size due tomode dependence */
doDefault = 1;
break;
default:
dbg("Error: COMMANDX out of range or table inconsistent (%c).\n",
cs->cmnd[0],lengths[cs->cmnd[0]] );
doDefault = 1;
}
if ( doDefault == 1 ) {
len = cs->data_payload_size;
}
dbg("doDefault %d, data length %u",doDefault, len);
return len;
}
//reset
/* This issues aBulk-only Reset to the device in question, including
* clearing the subsequent endpoint halts thatmay occur.
*/
intmyudisk_Bulk_reset(struct usb_device *udev, struct usb_interface *interface,
__u8bulk_in_endpointAddr, __u8 bulk_out_endpointAddr)
{
int result;
dbg("Bulk reset requested\n");
/* if the device was removed, then we'realready reset */
if (!udev)
return SUCCESS;
result = usb_control_msg(udev,
usb_sndctrlpipe(udev,0),
MU_BULK_RESET_REQUEST,
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0,
interface->altsetting[0].bInterfaceNumber,
NULL, 0, HZ*5);
if (result < 0) {
dbg("Bulk soft reset failed%d\n", result);
return FAILED;
}
/* long wait for reset */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ*6);
set_current_state(TASK_RUNNING);
usb_clear_halt(udev, usb_rcvbulkpipe(udev,bulk_in_endpointAddr));
usb_clear_halt(udev, usb_sndbulkpipe(udev,bulk_out_endpointAddr));
dbg("Bulk soft reset completed\n");
return SUCCESS;
}
//
//
intmyudisk_Bulk_transport(struct usb_device *udev, struct usb_interface*interface,
struct cmnd_struct*cs,
unsigned char *bulk_buffer,int bulk_size,
__u8bulk_in_endpointAddr, __u8 bulk_out_endpointAddr)
{
struct bulk_cb_wrap *bcb;
struct bulk_cs_wrap *bcs;
int result;
int pipe;
int partial;
char *buf;
unsigned int transfer_amount;
int ret = MYUDISK_TRANSPORT_ERROR;
bcb = kmalloc(sizeof *bcb, in_interrupt() ?GFP_ATOMIC : GFP_NOIO);
if (!bcb) {
return MYUDISK_TRANSPORT_ERROR;
}
bcs = kmalloc(sizeof *bcs, in_interrupt() ?GFP_ATOMIC : GFP_NOIO);
if (!bcs) {
kfree(bcb);
return MYUDISK_TRANSPORT_ERROR;
}
/* set up the command wrapper */
bcb->Signature =cpu_to_le32(MU_BULK_CB_SIGN);
transfer_amount = myudisk_transfer_length(cs);
bcb->DataTransferLength =cpu_to_le32(transfer_amount);
bcb->Flags = cs->sc_data_direction == SCSI_DATA_READ? 1 << 7 : 0;
bcb->Tag = ++(cs->tag);
bcb->Lun = cs->cmnd[1] >> 5;
bcb->Length = cs->cmd_len;
/* construct the pipe handle */
pipe = usb_sndbulkpipe(udev,bulk_out_endpointAddr);
/* copy the command payload */
memset(bcb->CDB, 0, sizeof(bcb->CDB));
memcpy(bcb->CDB, cs->cmnd,bcb->Length);
/* send it to out endpoint */
myudisk_debug_data ("command tosend", MU_BULK_CB_WRAP_LEN, (unsigned char *)bcb);
dbg("Bulk command S 0x%x T 0x%x Trg %dLUN %d L %d F %d CL %d",
le32_to_cpu(bcb->Signature), bcb->Tag,
(bcb->Lun >> 4), (bcb->Lun & 0x0F),
bcb->DataTransferLength, bcb->Flags, bcb->Length);
result = usb_bulk_msg(udev,
pipe,
bcb, MU_BULK_CB_WRAP_LEN,
&partial, MYUDISK_TIMEOUT);
dbg("Bulk command transfer result=%d,xferred %d/%d",
result, partial, MU_BULK_CB_WRAP_LEN);
/* if the command was aborted, indicate that*/
if (result == -ECONNRESET) {
ret = MYUDISK_TRANSPORT_ABORTED;
goto out;
}
/* if we stall, we need to clear it before wego on */
if (result == -EPIPE) {
dbg("clearing endpoint halt forpipe 0x%x", pipe);
result = usb_clear_halt(udev, pipe);
/* if the command was aborted, indicatethat */
if (result == -ECONNRESET) {
ret = MYUDISK_TRANSPORT_ABORTED;
goto out;
}
result = -EPIPE;
} else if (result) {
/* unknown error -- we've got a problem*/
ret = MYUDISK_TRANSPORT_ERROR;
goto out;
}
dbg("----cmnd end\n");
/* if the command transfered well, then we goto the data stage */
if (result == 0) {
/* send/receive data payload, if thereis any */
if (bcb->DataTransferLength) {
/* calculate the appropriatepipe and buffer information */
if (cs->sc_data_direction ==SCSI_DATA_READ) {
pipe =usb_rcvbulkpipe(udev, bulk_in_endpointAddr);
buf = bulk_buffer;
if (transfer_amount >bulk_size) {
dbg("illegalDataTransferLength!");
transfer_amount =bulk_size;
}
} else {
pipe =usb_sndbulkpipe(udev, bulk_out_endpointAddr);
buf = bulk_buffer;
if (transfer_amount >bulk_size) {
dbg("illegalDataTransferLength!");
transfer_amount =bulk_size;
}
}
/* transfer the data */
dbg("xfer %d bytesdata", transfer_amount);
result = usb_bulk_msg(udev,
pipe,
buf, transfer_amount,
&partial,MYUDISK_TIMEOUT);
cs->data_payload_actualSize =partial;
dbg("Bulk data transferresult: %d, xferred %d/%d",
result, partial, transfer_amount);
// myudisk_debug_data("data transfered", partial, buf);
/* if we stall, we need to clearit before we go on */
if (result == -EPIPE) {
dbg("clearingendpoint halt for pipe 0x%x", pipe);
usb_clear_halt(udev,pipe);
}
/* if it was aborted, we need toindicate that */
if (result) {
ret = MYUDISK_TRANSPORT_ABORTED;
goto out;
}
}
}
dbg("----data end\n");
/* See flow chart on pg 15 of the Bulk OnlyTransport spec for
* anexplanation of how this code works.
*/
/* construct the pipe handle */
pipe = usb_rcvbulkpipe(udev, bulk_in_endpointAddr);
/* get CSW for device status */
dbg("Attempting to get CSW...");
result = usb_bulk_msg(udev,
pipe,
bcs, MU_BULK_CS_WRAP_LEN,
&partial, MYUDISK_TIMEOUT);
dbg("Bulk status transfer result = %d,xferred %d/%d", result, partial, MU_BULK_CS_WRAP_LEN);
/* if the command was aborted, indicate that*/
if (result == -ECONNRESET) {
ret = MYUDISK_TRANSPORT_ABORTED;
goto out;
}
/* did the attempt to read the CSW fail? */
if (result == -EPIPE) {
dbg("clearing endpoint halt forpipe 0x%x", pipe);
result = usb_clear_halt(udev, pipe);
/* if the command was aborted, indicatethat */
if (result == -ECONNRESET) {
ret = MYUDISK_TRANSPORT_ABORTED;
goto out;
}
/* get the status again */
dbg("Attempting to get CSW (2ndtry)...");
result = usb_bulk_msg(udev,
pipe,
bcs, MU_BULK_CS_WRAP_LEN,
&partial,MYUDISK_TIMEOUT);
/* if the command was aborted, indicatethat */
if (result == -ECONNRESET) {
ret = MYUDISK_TRANSPORT_ABORTED;
goto out;
}
/* if it fails again, we need a resetand return an error*/
if (result == -EPIPE) {
dbg("clearing halt for pipe0x%x", pipe);
result = usb_clear_halt(udev,pipe);
/* if the command was aborted,indicate that */
if (result == -ECONNRESET) {
ret =MYUDISK_TRANSPORT_ABORTED;
} else {
ret =MYUDISK_TRANSPORT_ERROR;
}
goto out;
}
}
/* if we still have a failure at this point,we're in trouble */
if (result) {
ret = MYUDISK_TRANSPORT_ERROR;
goto out;
}
/* check bulk status */
myudisk_debug_data ("status datarecerived", partial, (unsigned char *)bcs);
dbg("Bulk status Sig 0x%x T 0x%x R %dStat 0x%x",
le32_to_cpu(bcs->Signature), bcs->Tag, bcs->Residue,bcs->Status);
if ((bcs->Signature != cpu_to_le32(MU_BULK_CS_SIGN)&& bcs->Signature != cpu_to_le32(MU_BULK_CS_OLYMPUS_SIGN)) ||
bcs->Tag != bcb->Tag ||
bcs->Status > MU_BULK_STAT_PHASE ||
partial != 13) {
dbg("Bulk logical error");
ret = MYUDISK_TRANSPORT_ERROR;
goto out;
}
/* based on the status code, we report good orbad */
switch (bcs->Status) {
case MU_BULK_STAT_OK:
/* command good -- note thatdata could be short */
ret = MYUDISK_TRANSPORT_GOOD;
goto out;
case MU_BULK_STAT_FAIL:
/* command failed */
ret = MYUDISK_TRANSPORT_FAILED;
goto out;
case MU_BULK_STAT_PHASE:
/* phase error -- note that atransport reset will be
* invoked by the invoke_transport() function
*/
ret = MYUDISK_TRANSPORT_ERROR;
goto out;
}
/* we should never get here, but if we do,we're in trouble */
dbg("we should never get here.");
out:
if (ret == MYUDISK_TRANSPORT_GOOD) {
dbg("transport indicates commandwas successful\n");
} else if (ret == MYUDISK_TRANSPORT_ABORTED ||
ret == MYUDISK_TRANSPORT_ERROR) {
myudisk_Bulk_reset(udev, interface,
bulk_in_endpointAddr,bulk_out_endpointAddr);
dbg("transport indicates commandwas aborted\n");
}
kfree(bcb);
kfree(bcs);
return ret;
}
//
static voidmyudisk_disconnect(struct usb_device *udev, void *ptr)
{
struct my_udisk *dev;
int minor;
dev = (struct my_udisk *)ptr;
down (&minor_table_mutex);
down (&dev->sem);
minor = dev->minor;
/* remove our devfs node */
devfs_unregister(dev->devfs);
/* if the device is not opened, then we cleanup right now */
if (!dev->open_count) {
up (&dev->sem);
myudisk_delete (dev);
} else {
dev->udev = NULL;
up (&dev->sem);
}
info("My UDisk #%d nowdisconnected", minor);
up (&minor_table_mutex);
}
/**
* my_udisk_init
*/
static int __initmy_udisk_init(void)
{
int result;
/* register this driver with the USB subsystem*/
result = usb_register(&myudisk_driver);
if (result < 0) {
err("usb_register failed for the"__FILE__" driver. Error number %d",
result);
return -1;
}
info(DRIVER_DESC " "DRIVER_VERSION);
/* 块大小必须是扇区大小的整数倍 */
/*if (myudisk_blocksize & ((1 << MYUDISK_SECTOR_BITS)-1)){
dbg("Block size not a multiple ofsector size\n");
return -EINVAL;
}*/
/* 分配存储空间 */
/*myudisk_storage = (char *)vmalloc(1024*myudisk_size);
if (myudisk_storage == NULL) {
dbg("Not enough memory. Try asmaller size.\n");
return -ENOMEM;
}*/
//memset(myudisk_storage, 0,1024*myudisk_size);
/* 【重要】向系统注册块设备 */
result = register_blkdev(MAJOR_NR,DEVICE_NAME, &myudisk_fops);
if (result) {
dbg("couldn't register blockdevice\n");
return result;
}
/* 在系统中注册块的大小、存储容量等参数 */
hardsect_size[MAJOR_NR] = &myudisk_hard;
blksize_size[MAJOR_NR] = &myudisk_soft;
blk_size[MAJOR_NR] = &myudisk_size;
/* 在系统中注册响应读写请求的函数 */
blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR),&myudisk_request);
read_ahead[MAJOR_NR] = myudisk_readahead;
return 0;
}
/**
* my_udisk_exit
*/
static void__exit my_udisk_exit(void)
{
/* deregister this driver with the USBsubsystem */
usb_deregister(&myudisk_driver);
unregister_blkdev(MAJOR_NR, DEVICE_NAME);
invalidate_buffers(MKDEV(MAJOR_NR,0));
/* remove our request function */
blk_dev[MAJOR_NR].request_queue.request_fn =0;
}
module_init(my_udisk_init);
module_exit(my_udisk_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
• 请特别留意内核函数usb_bulk_msg的用法和SCSI接口规范,以及磁盘分区表结构。
• 可能需要读一些英文原版资料。
• 检查时,你需要当场独立完成以下操作:
– 用lsmod命令查询系统中是否已经加载usb-storage的模块,这是Linux系统自带的U盘驱动程序。如果已经加载,则用rmmod命令卸载这个模块。
查看U盘出厂信息
– 编译改装后的radimo程序,要求能编译通过,允许出现编译警告。
– 用insmod命令加载改装后的radimo程序。
– 插入U盘。要求在用dmesg查看系统日志时,能够看到改装的radimo程序发现了U盘。允许你的程序只认识一种或者有限的几种U盘,不要求能够认出市面上所有U盘。
– 能够在系统日志中显示出U盘的容量。
– 用mknod命令建立设备文件,并用mount命令挂接这个U盘的文件系统到/mnt。你的U盘可以预先用Windows格式化好。
– 用df命令应该能够看到已经挂接好的U盘。
– 用ls /mnt命令应该能够看到U盘中的内容。用cp命令能够将U盘中的任意文件复制到硬盘上。
– 用umount命令和rmmod命令清理现场。