📄 myudisk.c
字号:
/* * My UDisk driver - 0.1 * * TODO: * - fix urb->status race condition in write sequence * - move minor_table to a dynamic list. * * History: * * 2008_01_9 - 0.1 - zero out dev in probe function 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 // 定义主设备号#define DEVICE_NAME "myudisk" // 定义设备名#define DEVICE_NR(device) (MINOR(device)) #define DEVICE_ON(device)#define DEVICE_OFF(device)#define DEVICE_NO_RANDOM#include <linux/blk.h>#define MYUDISK_SECTOR_BITS 9 /* 2**9 byte hardware sector */#define MYUDISK_BLOCK_SIZE 1024 /* block size */ //定义了一个块的大小,以字节为单位#define MYUDISK_TOTAL_SIZE 2048 /* size in blocks */ //定义了这个虚拟盘的容量,以块为单位/* the storage pool *///static char *myudisk_storage; //这个指针是全局变量,指向用于虚拟盘的内存//static int myudisk_sectorsize = 1 << MYUDISK_SECTOR_BITS;//static int myudisk_blocksize = MYUDISK_BLOCK_SIZE;static int myudisk_size ;static int myudisk_readahead = 4;#define CONFIG_USB_DEBUG#ifdef CONFIG_USB_DEBUG static int debug = 1;#else static int debug;#endif/* Use our own dbg macro */#undef dbg#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ "(%s): " format "\n" , __FUNCTION__, ## arg); } while (0)/* Version Information */#define DRIVER_VERSION "v0.1"#define DRIVER_AUTHOR "Wen Yan Jun, yjwen@nudt.edu.cn"#define DRIVER_DESC "My UDisk Driver"/* Module paramaters */MODULE_PARM(debug, "i");MODULE_PARM_DESC(debug, "Debug enabled or not");/* Define these values to match your device */#define MY_UDISK_VENDOR_ID 0x58f#define MY_UDISK_PRODUCT_ID 0x9380/* table of devices that work with this driver */static struct usb_device_id myudisk_table [] = { { USB_DEVICE(MY_UDISK_VENDOR_ID, MY_UDISK_PRODUCT_ID) }, { } /* Terminating entry */};MODULE_DEVICE_TABLE (usb, myudisk_table);/* Get a minor range for your devices from the usb maintainer */#define MY_UDISK_MINOR_BASE 192 /* we can have up to this number of device plugged in at once */#define MAX_DEVICES 16/* Structure to hold 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 number for 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 we have */ char num_bulk_out; /* number of bulk out endpoints we have */ 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; /* the buffer 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 been opened */ struct semaphore sem; /* locks this structure */};/* the global usb devfs handle */extern devfs_handle_t usb_devfs_handle;//////////////////////////////////////////////////////////////////////////////////////////////////////#define MAX_COMMAND_SIZE 16#define MU_BULK_BUFFER_LEN 4096 /* if the blocksize is larger than the buffer len, data transform will fail. *//* transport return codes */ #define MYUDISK_TRANSPORT_GOOD 0 /* Transport good, command good */#define MYUDISK_TRANSPORT_FAILED 1 /* Transport good, command failed */#define MYUDISK_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */#define MYUDISK_TRANSPORT_ABORTED 3 /* Transport aborted */#define SCSI_DATA_UNKNOWN 0#define SCSI_DATA_WRITE 1#define SCSI_DATA_READ 2#define SCSI_DATA_NONE 3static inline void 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");}struct cmnd_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 */};#define MYUDISK_TIMEOUT HZ*10#define MYUDISK_STRING_LEN 32#define SUCCESS 0x2002#define FAILED 0x2003/* command block wrapper */struct bulk_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 */};#define MU_BULK_CB_WRAP_LEN 31#define MU_BULK_CB_SIGN 0x43425355 /*spells out USBC */#define MU_BULK_FLAG_IN 1#define MU_BULK_FLAG_OUT 0/* bulk-only class specific requests */#define MU_BULK_RESET_REQUEST 0xff#define MU_BULK_GET_MAX_LUN 0xfe/* command status wrapper */struct bulk_cs_wrap { __u32 Signature; /* should = 'USBS' */ __u32 Tag; /* same as original command */ __u32 Residue; /* amount not transferred */ __u8 Status; /* see below */ __u8 Filler[18];};#define MU_BULK_CS_WRAP_LEN 13#define MU_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' *//* This is for Olympus Camedia digital cameras */#define MU_BULK_CS_OLYMPUS_SIGN 0x55425355 /* spells out 'USBU' */#define MU_BULK_STAT_OK 0#define MU_BULK_STAT_FAIL 1#define MU_BULK_STAT_PHASE 2/* bulk-only class specific requests */#define MU_BULK_RESET_REQUEST 0xff#define MU_BULK_GET_MAX_LUN 0xfe__u32 myudisk_soft ,SECTOR_num,BLOCK_num;unsigned long PARTION_offset;static int myudisk_hard = 1 << MYUDISK_SECTOR_BITS;/*********************************************************************** * USB mass storage device related functions ***********************************************************************///////////////////////////////////////////////////////////////////////////////////////////////////////* local function prototypes */static int myudisk_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static int myudisk_open (struct inode *inode, struct file *file);static int myudisk_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 void myudisk_disconnect (struct usb_device *dev, void *ptr);static void myudisk_write_bulk_callback (struct urb *urb);extern int myudisk_Bulk_transport(struct usb_device *udev, struct usb_interface *interface, struct cmnd_struct *cs, unsigned char *bulk_buffer, int bulk_size, __u8 bulk_in_endpointAddr, __u8 bulk_out_endpointAddr);unsigned int myudisk_transfer_length(struct cmnd_struct *ud);int myudisk_Bulk_reset(struct usb_device *udev, struct usb_interface *interface, __u8 bulk_in_endpointAddr, __u8 bulk_out_endpointAddr);/* array of pointers to our devices that are currently connected */static struct my_udisk *minor_table[MAX_DEVICES];/* lock to protect the minor_table structure */static DECLARE_MUTEX (minor_table_mutex);/* * File operations needed when we register this driver. * This assumes that this driver NEEDS file operations, * of course, which means that the driver is expected * to have a node in the /dev directory. If the USB * device were for a network interface then the driver * would use "struct net_driver" instead, and a serial * device would use "struct tty_driver". */static struct block_device_operations myudisk_fops = { ioctl: myudisk_ioctl, open: myudisk_open, release: myudisk_release,}; /* usb specific object needed to register this driver with the usb subsystem */static struct usb_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 inline void 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 inline void 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 int myudisk_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 data for 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 private structure */ 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 int myudisk_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 the file 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 might be 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 int myudisk_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; return put_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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -