/*************************************************************************** usb_mass.c - description ------------------- begin : Wed Feb 12 2003 copyright : (C) 2003 by Ahmed Abdel-Hamid email : ahmedam@mail.usa.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static kdev_t dev; static int myblk_size; struct CBW{ //command block __u32 dCBWSignature; __u32 dCBWTag; __u32 dCBWDataTransfereLength; __u8 bmCBWFlags; __u8 bCBWLUN; __u8 bCBWCBLength; __u8 CBWCB[15]; }__attribute__ ((packed)); struct CSW{ //command status __u32 dCSWSignature; __u32 dCSWTag; __u32 dCSWDataResidue; __u8 bCSWStatus; }__attribute__ ((packed)); static __u32 extractInt32(__u8 in[],int start) { return ((__u32)in[start])<<24|((__u32)in[start+1])<<16|((__u32)in[start+2])<<8|in[start+3]; } static __u32 extractInt16(__u8 in[],int start) { return ((__u32)in[start])<<8|((__u32)in[start+1]); } static void setInt(__u8 in[],int start,__u32 val) { in[start]=(__u8)(val>>24); in[start+1]=(__u8)(val>>16); in[start+2]=(__u8)(val>>8); in[start+3]=(__u8)val; } static __u32 usbmassblock_numBlocks; static void (*usbmassblock_callback)(int size); static void b_end_io(struct buffer_head *bh, int uptodate) { usbmassblock_callback(myblk_size); } static struct buffer_head bh; static void generate_request(int read,char *buf,int pos) { static int count=0; if(usbmassblock_numBlocks==0){ // printk(" blocks exceeded\n"); usbmassblock_callback(0); return; } usbmassblock_numBlocks--; bh.b_size=myblk_size; bh.b_rdev=dev; bh.b_data=buf; bh.b_end_io=b_end_io; bh.b_private=NULL; bh.b_rsector=pos; bh.b_state=1UL<0) { data_remain_size-=size; if(data_remain_size<0) data_remain_size=0; if(data_remain_size==0){ if(usbmass_call_back){ usbmass_call_back(usbmass_send_callback2,usbmass_buf,MAXBUFSIZE,&usbmass_callback_current_pos); return; } if(data_remain_size==0) //the buffer is pre-prepared data_remain_size=MAXBUFSIZE0) { data_remain_size+=size; if(data_remain_size0){ if(usbmass_call_back) usbmass_call_back(usbmass_receive_callback2,usbmass_buf,data_remain_size,&usbmass_callback_current_pos); else{ data_remain_size=0; usbmass_receive_callback(0,0); } }else{ data_remain_size=0; usbmass_send_status(usbmass_status); } } /* call_back: will be called each time new data is needed/available usb_callback: must be called after call_back done reading/writing pos: the initial value is equal to init_pos maxsize: the buffer size return: the number of bytes actually added to the buffer */ static void usbmass_sendreceive(void (*call_back)(void (*usb_callback)(int size),char *buf,int maxsize,int *pos),int init_pos){ usbmass_call_back=call_back; usbmass_callback_current_pos=init_pos; data_remain_size=0; if(usbmass_data_direction){ usbmass_send_callback(0,0); }else{ usbmass_receive_callback(0,0); } } static void usbmass_done(int flag,int size){ sa1100_usb_recv(usbmass_buf,MAXBUFSIZE,usbmass_recv_command_callback); } static void usbmass_send_status(int status){ struct CSW *d; usbmass_status=status; //first send/receive the required size if(usbmass_data_size>0){ usbmass_sendreceive(NULL,0); return; } d=(struct CSW*)usbmass_buf; d->dCSWSignature=0x53425355; d->dCSWTag=tag; d->dCSWDataResidue=0; d->bCSWStatus=usbmass_status?1:0; sa1100_usb_send((char *)d,13,usbmass_done); } static void usbmass_recv_command_callback(int flag, int size) { usbmass_status=0; // printk("usbstorage: got %d bytes\n",size); if(size==31){ struct CBW *cbw; cbw=(struct CBW*)usbmass_buf; // printk(" Going to interpret a command, signature=%X\n",cbw->dCBWSignature); if(cbw->dCBWSignature==0x43425355){ tag=cbw->dCBWTag; usbmass_data_size=cbw->dCBWDataTransfereLength; usbmass_data_direction=cbw->bmCBWFlags&0x80; // printk(" Valid Signature tag=%ud toreceive=%ud command=%x\n",tag,usbmass_data_size,(unsigned)cbw->CBWCB[0]); switch(cbw->CBWCB[0]){ case FORMAT_UNIT: //format unit break; case INQUIRY: //Inquiry usbmass_buf[0]=0; //0xe; usbmass_buf[1]=0; //0:non removable, 0x80: removable usbmass_buf[2]=0x04; //comply with spc2r20 usbmass_buf[3]=0x0; usbmass_buf[3]|=2; //data in spc2r20 format usbmass_buf[4]=55; //additional length usbmass_buf[5]=0; usbmass_buf[6]=0; usbmass_buf[7]=0; memcpy(&usbmass_buf[8],"iPAQ USB",8); memcpy(&usbmass_buf[16],"USB Mass Storage ",16); memcpy(&usbmass_buf[32],"0.10",4); usbmass_buf[58]=0x02; usbmass_buf[59]=0x20; usbmass_sendreceive(NULL,0); return; case MODE_SELECT: //mode select break; case MODE_SENSE: //mode sense break; case PERSISTENT_RESERVE_IN: //persistent reserve in break; case PERSISTENT_RESERVE_OUT: //persistent reserve out break; case ALLOW_MEDIUM_REMOVAL: //allow medium removal break; case READ_10: //read usbmassblock_numBlocks=extractInt16(cbw->CBWCB,7); // printk(" blocks=%d\n",usbmassblock_numBlocks); usbmass_sendreceive(usbmassblock_read,extractInt32(cbw->CBWCB,2)); return; case READ_CAPACITY: //read capacity setInt(usbmass_buf,0,blk_size[MAJOR(dev)][MINOR(dev)]<<1); setInt(usbmass_buf,4,myblk_size); usbmass_sendreceive(NULL,0); return; case RELEASE: //release break; case REQUEST_SENSE: //requist sense memset(usbmass_buf,0,18); usbmass_buf[0]=0xf0; usbmass_buf[2]=usbmass_status&0x8; usbmass_sendreceive(NULL,0); return; case RESERVE: //reserve break; case START_STOP: //start stop unit break; case SYNCHRONIZE_CACHE: //synchronize cache break; case TEST_UNIT_READY: //test unit ready usbmass_send_status(0); return; case VERIFY: //verify break; case WRITE_10: //write usbmassblock_numBlocks=extractInt16(cbw->CBWCB,7); // printk(" blocks=%d\n",usbmassblock_numBlocks); usbmass_sendreceive(usbmassblock_write,extractInt32(cbw->CBWCB,2)); return; case WRITE_BUFFER: //write buffer break; } printk(" Unimplemented command %x\n",(unsigned)cbw->CBWCB[0]); usbmass_send_status(5); //command failed return; }else{ printk(" Invalid Signature\n"); } } sa1100_usb_recv(usbmass_buf,MAXBUFSIZE,usbmass_recv_command_callback); } static int dev_major=254; static int dev_minor=0; static struct block_device *bdev=NULL; static int __init usbmass_init(void) { int rc; printk("Module usbmass_init inserted\n"); dev=MKDEV(dev_major,dev_minor); bdev = bdget(kdev_t_to_nr(dev)); if(!bdev){ printk("Can't find device %d %d\n",dev_major,dev_minor); return -ENODEV; } rc=blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_RAW); if(rc){ printk("Can't allocate device %d %d\n",dev_major,dev_minor); return -ENODEV; } usbmass_buf= kmalloc(MAXBUFSIZE+512, GFP_KERNEL | GFP_DMA ); if (!usbmass_buf) return -ENOMEM; rc = sa1100_usb_open( "usb-mass" ); if ( rc == 0 ) { string_desc_t * pstr; desc_t * pd = sa1100_usb_get_descriptor_ptr(); pd->b.ep1.wMaxPacketSize = make_word( 64 ); pd->b.ep2.wMaxPacketSize = make_word( 64 ); pd->b.cfg.bmAttributes = USB_CONFIG_SELFPOWERED; pd->b.cfg.MaxPower = 0; pd->dev.idVendor = 0x1112; pd->dev.idProduct = 0x44; pd->b.intf.bInterfaceClass = 0x08; //Mass Storage Class pd->b.intf.bInterfaceSubClass = 0x06; //0x01; // Reduced block command pd->b.intf.bInterfaceProtocol = 0x50; // bulk only interface pd->b.intf.iInterface=1; pd->b.intf.bInterfaceNumber=0; pstr = sa1100_usb_kmalloc_string_descriptor( "SA1100 USB STORAGE" ); if ( pstr ) { sa1100_usb_set_string_descriptor( 1, pstr ); pd->dev.iProduct = 1; } myblk_size=hardsect_size[MAJOR(dev)]?hardsect_size[MAJOR(dev)][MINOR(dev)]:512; printk("block size=%d\n",myblk_size); rc = sa1100_usb_start(); sa1100_usb_recv(usbmass_buf,MAXBUFSIZE,usbmass_recv_command_callback); } return rc; } static void __exit usbmass_exit(void) { string_desc_t * pstr; printk("Module usbmass_init removed\n"); sa1100_usb_stop(); sa1100_usb_close(); if ( (pstr = sa1100_usb_get_string_descriptor(1)) != NULL ) kfree( pstr ); kfree(usbmass_buf); if(bdev){ blkdev_put(bdev, BDEV_RAW); } } module_init(usbmass_init); module_exit(usbmass_exit); MODULE_PARM(dev_major,"i"); MODULE_PARM(dev_minor,"i"); MODULE_AUTHOR("Ahmed Abdel-Hamid"); MODULE_DESCRIPTION("USB Mass Storage Device"); EXPORT_NO_SYMBOLS;