Hi,
This is the first submission of the new PS3 storage drivers:
[1] ps3: Preallocate bootmem memory for the PS3 FLASH ROM storage driver
[2] ps3: Extract ps3_repository_find_bus()
[3] ps3: Storage Driver Core
[4] ps3: Storage Driver Probing
[5] ps3: Disk Storage Driver
[6] ps3: ROM Storage Driver
[7] ps3: FLASH ROM Storage Driver
They are submitted purely for review, as some of the underlying infrastructure
code hasn't been submitted yet (for reference, you can take a look at Geoff's
git tree (git://git.kernel.org/pub/scm/linux/kernel/git/geoff/ps3-linux.git)).
Thanks for you comments!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
--
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
@@ -508,6 +509,348 @@ static int __devinit ps3_register_av(voireturnresult;}+#ifdef DEBUG+staticconstchar*ps3stor_dev_type(enumps3_dev_typedev_type)+{+switch(dev_type){+casePS3_DEV_TYPE_STOR_DISK:+return"disk";++casePS3_DEV_TYPE_STOR_ROM:+return"rom";++casePS3_DEV_TYPE_STOR_FLASH:+return"flash";++casePS3_DEV_TYPE_NONE:+return"not present";++default:+return"unknown";+}+}+#else+staticinlineconstchar*ps3stor_dev_type(enumps3_dev_typedev_type)+{+returnNULL;+}+#endif /* DEBUG */++#define NOTIFICATION_DEVID ((u64)(-1L))+#define NOTIFICATION_TIMEOUT HZ++staticu64ps3stor_wait_for_completion(u64devid,u64tag,+unsignedinttimeout)+{+unsignedintretries=0;+u64res=-1,status;++for(retries=0;retries<timeout;retries++){+res=lv1_storage_check_async_status(NOTIFICATION_DEVID,tag,+&status);+if(!res)+break;+set_current_state(TASK_INTERRUPTIBLE);+schedule_timeout(1);+}+if(res)+pr_debug("%s:%u: check_async_status returns %ld status %lx\n",+__func__,__LINE__,res,status);++returnres;+}++staticintps3stor_probe_notification(structps3_storage_device*dev,+enumps3_dev_typedev_type)+{+interror=-ENODEV,res;+u64*buf;+u64lpar;++pr_info("%s:%u: Requesting notification\n",__func__,__LINE__);++buf=kzalloc(512,GFP_KERNEL);+if(!buf)+return-ENOMEM;++lpar=ps3_mm_phys_to_lpar(__pa(buf));++/* 2-1) open special event device */+res=lv1_open_device(dev->sbd.did.bus_id,NOTIFICATION_DEVID,0);+if(res){+printk(KERN_ERR"%s:%u: open notification device failed %d\n",+__func__,__LINE__,res);+gotofail_free;+}++/* 2-2) write info to request notify */+buf[0]=0;+buf[1]=(1<<1);/* region update info only */+res=lv1_storage_write(NOTIFICATION_DEVID,0,0,1,0,lpar,+&dev->tag);+if(res){+printk(KERN_ERR"%s:%u: notify request write failed %d\n",+__func__,__LINE__,res);+gotofail_close;+}++/* wait for completion in one second */+res=ps3stor_wait_for_completion(NOTIFICATION_DEVID,dev->tag,+NOTIFICATION_TIMEOUT);+if(res){+/* write not completed */+printk(KERN_ERR"%s:%u: write not completed %d\n",__func__,+__LINE__,res);+gotofail_close;+}++/* 2-3) read to wait region notification for each device */+while(1){+memset(buf,0,512);+lv1_storage_read(NOTIFICATION_DEVID,0,0,1,0,lpar,+&dev->tag);+res=ps3stor_wait_for_completion(NOTIFICATION_DEVID,dev->tag,+NOTIFICATION_TIMEOUT);+if(res){+/* read not completed */+printk(KERN_ERR"%s:%u: read not completed %d\n",+__func__,__LINE__,res);+break;+}++/* 2-4) verify the notification */+if(buf[0]!=1||buf[1]!=dev->sbd.did.bus_id){+/* other info notified */+pr_debug("%s:%u: notification info %ld dev=%lx type=%lx\n",+__func__,__LINE__,buf[0],buf[2],buf[3]);+break;+}++if(buf[2]==dev->sbd.did.dev_id&&buf[3]==dev_type){+pr_debug("%s:%u: device ready\n",__func__,__LINE__);+error=0;+break;+}+}++fail_close:+lv1_close_device(dev->sbd.did.bus_id,NOTIFICATION_DEVID);++fail_free:+kfree(buf);+returnerror;+}++staticintps3stor_probe_dev(structps3_repository_device*repo)+{+interror;+u64port,blk_size,num_blocks;+unsignedintnum_regions,i;+structps3_storage_device*dev;+enumps3_dev_typedev_type;+unsignedintmatch_id;++pr_info("%s:%u: Probing new storage device %u\n",__func__,__LINE__,+repo->dev_index);++error=ps3_repository_read_dev_id(repo->bus_index,repo->dev_index,+&repo->did.dev_id);+if(error){+printk(KERN_ERR"%s:%u: read_dev_id failed %d\n",__func__,+__LINE__,error);+return-ENODEV;+}++error=ps3_repository_read_dev_type(repo->bus_index,repo->dev_index,+&dev_type);+if(error){+printk(KERN_ERR"%s:%u: read_dev_type failed %d\n",__func__,+__LINE__,error);+return-ENODEV;+}++pr_debug("%s:%u: index %u:%u: id %u:%u dev_type %u (%s)\n",__func__,+__LINE__,repo->bus_index,repo->dev_index,repo->did.bus_id,+repo->did.dev_id,dev_type,ps3stor_dev_type(dev_type));++switch(dev_type){+casePS3_DEV_TYPE_STOR_DISK:+match_id=PS3_MATCH_ID_STOR_DISK;+break;++casePS3_DEV_TYPE_STOR_ROM:+match_id=PS3_MATCH_ID_STOR_ROM;+break;++casePS3_DEV_TYPE_STOR_FLASH:+match_id=PS3_MATCH_ID_STOR_FLASH;+break;++default:+return0;+}++error=ps3_repository_read_stor_dev_info(repo->bus_index,+repo->dev_index,&port,+&blk_size,&num_blocks,+&num_regions);+if(error){+printk(KERN_ERR"%s:%u: _read_stor_dev_info failed %d\n",+__func__,__LINE__,error);+return-ENODEV;+}+pr_debug("%s:%u: index %u:%u: port %lu blk_size %lu num_blocks %lu "+"num_regions %u\n",+__func__,__LINE__,repo->bus_index,repo->dev_index,port,+blk_size,num_blocks,num_regions);++dev=kzalloc(sizeof(structps3_storage_device)++num_regions*sizeof(structps3_storage_region),+GFP_KERNEL);+if(!dev)+return-ENOMEM;++dev->sbd.did=repo->did;+ps3_system_bus_device_init(&dev->sbd,match_id,&dev->dma_region,+NULL);+dev->blk_size=blk_size;+dev->num_regions=num_regions;++error=ps3_repository_find_interrupt(repo,+PS3_INTERRUPT_TYPE_EVENT_PORT,+&dev->sbd.interrupt_id);+if(error){+printk(KERN_ERR"%s:%u: find_interrupt failed %d\n",__func__,+__LINE__,error);+gotocleanup;+}++/* FIXME Do we really need this? I guess for kboot only? */+error=ps3stor_probe_notification(dev,dev_type);+if(error){+printk(KERN_ERR"%s:%u: probe_notification failed %d\n",+__func__,__LINE__,error);+gotocleanup;+}++for(i=0;i<num_regions;i++){+unsignedintid;+u64start,size;++error=ps3_repository_read_stor_dev_region(repo->bus_index,+repo->dev_index,i,+&id,&start,+&size);+if(error){+printk(KERN_ERR+"%s:%u: read_stor_dev_region failed %d\n",+__func__,__LINE__,error);+gotocleanup;+}+pr_debug("%s:%u: region %u: id %u start %lu size %lu\n",+__func__,__LINE__,i,id,start,size);++dev->regions[i].id=id;+dev->regions[i].start=start;+dev->regions[i].size=size;+}++error=ps3_system_bus_device_register(&dev->sbd,PS3_IOBUS_SB);+if(error){+printk(KERN_ERR+"%s:%u: ps3_system_bus_device_register failed %d\n",+__func__,__LINE__,error);+gotocleanup;+}+return0;++cleanup:+kfree(dev);+return-ENODEV;+}++staticintps3stor_thread(void*data)+{+structps3_repository_device*repo=data;+interror;+unsignedintn,ms=250;++pr_debug("%s:%u: kthread started\n",__func__,__LINE__);++do{+try_to_freeze();++// pr_debug("%s:%u: Checking for new storage devices...\n",+// __func__, __LINE__);+error=ps3_repository_read_bus_num_dev(repo->bus_index,&n);+if(error){+printk(KERN_ERR"%s:%u: read_bus_num_dev failed %d\n",+__func__,__LINE__,error);+break;+}++if(n>repo->dev_index){+pr_debug("%s:%u: Found %u storage devices (%u new)\n",+__func__,__LINE__,n,n-repo->dev_index);++while(repo->dev_index<n&&!error){+error=ps3stor_probe_dev(repo);+repo->dev_index++;+}++ms=250;+}++msleep_interruptible(ms);+if(ms<60000)+ms<<=1;+}while(!kthread_should_stop());++pr_debug("%s:%u: kthread finished\n",__func__,__LINE__);++return0;+}++staticint__devinitps3_register_storage_devices(void)+{+interror;+staticstructps3_repository_devicerepo;+structtask_struct*task;++if(!firmware_has_feature(FW_FEATURE_PS3_LV1))+return-ENODEV;++error=ps3_repository_find_bus(PS3_BUS_TYPE_STORAGE,0,+&repo.bus_index);+if(error){+printk(KERN_ERR"%s: Cannot find storage bus (%d)\n",__func__,+error);+return-ENODEV;+}+pr_debug("%s:%u: Storage bus has index %u\n",__func__,__LINE__,+repo.bus_index);++error=ps3_repository_read_bus_id(repo.bus_index,&repo.did.bus_id);+if(error){+printk(KERN_ERR"%s: read_bus_id failed %d\n",__func__,+error);+return-ENODEV;+}++pr_debug("%s:%u: Storage bus has id %u\n",__func__,__LINE__,+repo.did.bus_id);++task=kthread_run(ps3stor_thread,&repo,"ps3stor-probe");+if(IS_ERR(task)){+error=PTR_ERR(task);+printk(KERN_ERR"%s: kthread_run failed %d\n",__func__,+error);+returnerror;+}++return0;+}+staticint__devinitps3_register_fb(void){interror;
@@ -556,6 +899,7 @@ static int __init ps3_register_known_devresult=ps3_register_sys_manager();result=ps3_register_sound();result=ps3_register_gelic();+result=ps3_register_storage_devices();pr_debug(" <- %s:%d\n",__func__,__LINE__);returnresult;
--
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
Repository updates:
- Extract ps3_repository_find_bus() from ps3_repository_find_device(), as the
storage driver needs it
- Make ps3_repository_find_device() return -ENODEV if a device is not found,
just like if a bus is not found
Signed-off-by: Geert Uytterhoeven <redacted>
Signed-off-by: Geoff Levand <redacted>
---
arch/powerpc/platforms/ps3/platform.h | 2 +
arch/powerpc/platforms/ps3/repository.c | 50 ++++++++++++++++++++------------
2 files changed, 34 insertions(+), 18 deletions(-)
@@ -541,7 +566,7 @@ static int find_device(unsigned int bus_}if(dev_index==num_dev)-return-1;+return-ENODEV;pr_debug("%s:%d: found dev_type %u at dev_index %u\n",__func__,__LINE__,dev_type,dev_index);
@@ -577,25 +602,14 @@ int ps3_repository_find_device (enum ps3BUG_ON(start_dev&&start_dev->bus_index>10);-for(bus_index=start_dev?start_dev->bus_index:0;bus_index<10;-bus_index++){-enumps3_bus_typex;--result=ps3_repository_read_bus_type(bus_index,&x);--if(result){-pr_debug("%s:%d read_bus_type failed\n",-__func__,__LINE__);-dev->bus_index=UINT_MAX;-returnresult;-}-if(x==bus_type)-break;+result=ps3_repository_find_bus(bus_type,+start_dev?start_dev->bus_index:0,+&bus_index);+if(result){+dev->bus_index=UINT_MAX;+returnresult;}-if(bus_index>=10)-return-ENODEV;-pr_debug("%s:%d: found bus_type %u at bus_index %u\n",__func__,__LINE__,bus_type,bus_index);
--
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
@@ -0,0 +1,333 @@+/*+*PS3StorageLibrary+*+*Copyright(C)2007SonyComputerEntertainmentInc.+*Copyright2007SonyCorp.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseaspublished+*bytheFreeSoftwareFoundation;version2oftheLicense.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,but+*WITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.SeetheGNU+*GeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicensealong+*withthisprogram;ifnot,writetotheFreeSoftwareFoundation,Inc.,+*51FranklinStreet,FifthFloor,Boston,MA02110-1301USA.+*/++#include <linux/dma-mapping.h>+#include <linux/interrupt.h>++#include <asm/lv1call.h>+#include <asm/ps3stor.h>+++staticirqreturn_tps3stor_interrupt(intirq,void*data)+{+structps3_storage_device*dev=data;++dev->lv1_res=lv1_storage_get_async_status(dev->sbd.did.dev_id,+&dev->lv1_tag,+&dev->lv1_status);+/*+*lv1_status=-1maymeanthatATAPItransportcompletedOK,but+*ATAPIcommanditselfresultedCHECKCONDITION+*so,upperlayershouldissueREQUEST_SENSEtocheckthesensedata+*/++if(dev->lv1_tag!=dev->tag)+dev_err(&dev->sbd.core,+"%s:%u: tag mismatch, got %lx, expected %lx\n",+__func__,__LINE__,dev->lv1_tag,dev->tag);+if(dev->lv1_res)+dev_err(&dev->sbd.core,"%s:%u: res=%d status=0x%lx\n",+__func__,__LINE__,dev->lv1_res,dev->lv1_status);+else+complete(&dev->irq_done);+returnIRQ_HANDLED;+}+++staticintps3stor_probe_access(structps3_storage_device*dev)+{+intres,error;+unsignedinti;+unsignedlongn;++if(dev->sbd.match_id==PS3_MATCH_ID_STOR_ROM){+/*specialcase:CD-ROMisassumedalwaysaccessible*/+dev->accessible_regions=1;+return0;+}++error=-EPERM;+for(i=0;i<dev->num_regions;i++){+dev_dbg(&dev->sbd.core,+"%s:%u: checking accessibility of region %u\n",+__func__,__LINE__,i);++dev->region_idx=i;+res=ps3stor_read_write_sectors(dev,dev->bounce_lpar,0,1,+0);+if(res){+dev_dbg(&dev->sbd.core,+"%s:%u: read failed, region %u is not accessible\n",+__func__,__LINE__,i);+continue;+}++dev_dbg(&dev->sbd.core,"%s:%u: region %u is accessible\n",+__func__,__LINE__,i);+set_bit(i,&dev->accessible_regions);++/*Wecanaccessatleastoneregion*/+error=0;+}+if(error)+returnerror;++n=hweight_long(dev->accessible_regions);+if(n>1)+dev_info(&dev->sbd.core,+"%s:%u: %lu accessible regions found. Only the first "+"one will be used",+__func__,__LINE__,n);+dev->region_idx=__ffs(dev->accessible_regions);+dev_dbg(&dev->sbd.core,+"First accessible region has index %u start %lu size %lu\n",+dev->region_idx,dev->regions[dev->region_idx].start,+dev->regions[dev->region_idx].size);++return0;+}+++/**+*ps3stor_setup-Setupastoragedevicebeforeuse+*@dev:Pointertoastructps3_storage_device+*@name:Nameofthestoragedriver+*+*Returns0forsuccess,oranerrorcode+*/+intps3stor_setup(structps3_storage_device*dev,constchar*name)+{+interror,res,alignment;+enumps3_dma_page_sizepage_size;++error=ps3_open_hv_device(&dev->sbd);+if(error){+dev_err(&dev->sbd.core,+"%s:%u: ps3_open_hv_device failed %d\n",__func__,+__LINE__,error);+gotofail;+}++error=ps3_sb_event_receive_port_setup(PS3_BINDING_CPU_ANY,+&dev->sbd.did,+dev->sbd.interrupt_id,+&dev->irq);+if(error){+dev_err(&dev->sbd.core,+"%s:%u: ps3_sb_event_receive_port_setup failed %d\n",+__func__,__LINE__,error);+gotofail_close_device;+}++error=request_irq(dev->irq,ps3stor_interrupt,IRQF_DISABLED,name,+dev);+if(error){+dev_err(&dev->sbd.core,"%s:%u: request_irq failed %d\n",+__func__,__LINE__,error);+gotofail_sb_event_receive_port_destroy;+}++alignment=min(__ffs(dev->bounce_size),+__ffs((unsignedlong)dev->bounce_buf));+if(alignment<12){+dev_err(&dev->sbd.core,+"%s:%u: bounce buffer not aligned (%lx at 0x%p)\n",+__func__,__LINE__,dev->bounce_size,dev->bounce_buf);+error=-EINVAL;+gotofail_free_irq;+}elseif(alignment<16)+page_size=PS3_DMA_4K;+else+page_size=PS3_DMA_64K;+dev->sbd.d_region=&dev->dma_region;+ps3_dma_region_init(&dev->dma_region,&dev->sbd.did,page_size,+PS3_DMA_OTHER,dev->bounce_buf,dev->bounce_size,+PS3_IOBUS_SB);+res=ps3_dma_region_create(&dev->dma_region);+if(res){+dev_err(&dev->sbd.core,"%s:%u: cannot create DMA region\n",+__func__,__LINE__);+error=-ENOMEM;+gotofail_free_irq;+}++dev->bounce_lpar=ps3_mm_phys_to_lpar(__pa(dev->bounce_buf));+dev->bounce_dma=dma_map_single(&dev->sbd.core,dev->bounce_buf,+dev->bounce_size,DMA_BIDIRECTIONAL);+if(!dev->bounce_dma){+dev_err(&dev->sbd.core,"%s:%u: map DMA region failed\n",+__func__,__LINE__);+error=-ENODEV;+gotofail_free_dma;+}++error=ps3stor_probe_access(dev);+if(error){+dev_err(&dev->sbd.core,"%s:%u: No accessible regions found\n",+__func__,__LINE__);+gotofail_unmap_dma;+}+return0;++fail_unmap_dma:+dma_unmap_single(&dev->sbd.core,dev->bounce_dma,dev->bounce_size,+DMA_BIDIRECTIONAL);+fail_free_dma:+ps3_dma_region_free(&dev->dma_region);+fail_free_irq:+free_irq(dev->irq,dev);+fail_sb_event_receive_port_destroy:+ps3_sb_event_receive_port_destroy(&dev->sbd.did,dev->sbd.interrupt_id,+dev->irq);+fail_close_device:+ps3_close_hv_device(&dev->sbd);+fail:+returnerror;+}+EXPORT_SYMBOL_GPL(ps3stor_setup);+++/**+*ps3stor_teardown-Teardownastoragedeviceafteruse+*@dev:Pointertoastructps3_storage_device+*/+voidps3stor_teardown(structps3_storage_device*dev)+{+interror;++dma_unmap_single(&dev->sbd.core,dev->bounce_dma,dev->bounce_size,+DMA_BIDIRECTIONAL);+ps3_dma_region_free(&dev->dma_region);++free_irq(dev->irq,dev);++error=ps3_sb_event_receive_port_destroy(&dev->sbd.did,+dev->sbd.interrupt_id,+dev->irq);+if(error)+dev_err(&dev->sbd.core,+"%s:%u: destroy event receive port failed %d\n",+__func__,__LINE__,error);++error=ps3_close_hv_device(&dev->sbd);+if(error)+dev_err(&dev->sbd.core,+"%s:%u: ps3_close_hv_device failed %d\n",__func__,+__LINE__,error);+}+EXPORT_SYMBOL_GPL(ps3stor_teardown);+++/**+*ps3stor_read_write_sectors-read/writefrom/toastoragedevice+*@dev:Pointertoastructps3_storage_device+*@lpar:HVlogicalpartitionaddress+*@start_sector:Firstsectortoread/write+*@sectors:Numberofsectorstoread/write+*@write:Flagindicatingwrite(non-zero)orread(zero)+*+*Returns0forsuccess,-1incaseoffailuretosubmitthecommand,or+*anLV1statusvalueincaseofothererrors+*/+u64ps3stor_read_write_sectors(structps3_storage_device*dev,u64lpar,+u64start_sector,u64sectors,intwrite)+{+unsignedintregion_id=dev->regions[dev->region_idx].id;+constchar*op=write?"write":"read";+intres;++dev_dbg(&dev->sbd.core,"%s:%u: %s %lu sectors starting at %lu\n",+__func__,__LINE__,op,sectors,start_sector);++init_completion(&dev->irq_done);+res=write?lv1_storage_write(dev->sbd.did.dev_id,region_id,+start_sector,sectors,0,lpar,+&dev->tag)+:lv1_storage_read(dev->sbd.did.dev_id,region_id,+start_sector,sectors,0,lpar,+&dev->tag);+if(res){+dev_dbg(&dev->sbd.core,"%s:%u: %s failed %d\n",__func__,+__LINE__,op,res);+return-1;+}++wait_for_completion(&dev->irq_done);+if(dev->lv1_status){+dev_dbg(&dev->sbd.core,"%s:%u: %s failed 0x%lx\n",__func__,+__LINE__,op,dev->lv1_status);+returndev->lv1_status;+}++dev_dbg(&dev->sbd.core,"%s:%u: %s completed\n",__func__,__LINE__,+op);++return0;+}+EXPORT_SYMBOL_GPL(ps3stor_read_write_sectors);+++/**+*ps3stor_send_command-sendadevicecommandtoastoragedevice+*@dev:Pointertoastructps3_storage_device+*@cmd:Commandnumber+*@arg1:Firstcommandargument+*@arg2:Secondcommandargument+*@arg3:Thirdcommandargument+*@arg4:Fourthcommandargument+*+*Returns0forsuccess,-1incaseoffailuretosubmitthecommand,or+*anLV1statusvalueincaseofothererrors+*/+u64ps3stor_send_command(structps3_storage_device*dev,u64cmd,u64arg1,+u64arg2,u64arg3,u64arg4)+{+intres;++dev_dbg(&dev->sbd.core,"%s:%u: send device command 0x%lx\n",__func__,+__LINE__,cmd);++init_completion(&dev->irq_done);++res=lv1_storage_send_device_command(dev->sbd.did.dev_id,cmd,arg1,+arg2,arg3,arg4,&dev->tag);+if(res){+dev_err(&dev->sbd.core,+"%s:%u: send_device_command 0x%lx failed %d\n",+__func__,__LINE__,cmd,res);+return-1;+}++wait_for_completion(&dev->irq_done);+if(dev->lv1_status)+dev_dbg(&dev->sbd.core,"%s:%u: command 0x%lx failed 0x%lx\n",+__func__,__LINE__,cmd,dev->lv1_status);+else+dev_dbg(&dev->sbd.core,"%s:%u: command 0x%lx completed\n",+__func__,__LINE__,cmd);++returndev->lv1_status;+}+EXPORT_SYMBOL_GPL(ps3stor_send_command);+++MODULE_LICENSE("GPL");+MODULE_DESCRIPTION("PS3StorageBusLibrary");+MODULE_AUTHOR("SonyCorporation");---/dev/null+++b/include/asm-powerpc/ps3stor.h
--
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
Add a FLASH ROM Storage Driver for the PS3:
- Implemented as a misc character device driver
- Uses a fixed 256 KiB buffer allocated from boot memory as the hypervisor
requires the writing of aligned 256 KiB blocks
Signed-off-by: Geert Uytterhoeven <redacted>
---
arch/powerpc/platforms/ps3/Kconfig | 12 +
drivers/char/Makefile | 2
drivers/char/ps3flash.c | 400 +++++++++++++++++++++++++++++++++++++
3 files changed, 414 insertions(+)
@@ -104,6 +104,8 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/ obj-$(CONFIG_HANGCHECK_TIMER)+=hangcheck-timer.o obj-$(CONFIG_TCG_TPM)+=tpm/+obj-$(CONFIG_PS3_FLASH)+=ps3flash.o+# Files generated that shall be removed upon make clean clean-files:=consolemap_deftbl.cdefkeymap.c---/dev/null+++b/drivers/char/ps3flash.c
@@ -0,0 +1,400 @@+/*+*PS3FLASHROMStorageDriver+*+*Copyright(C)2007SonyComputerEntertainmentInc.+*Copyright2007SonyCorp.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseaspublished+*bytheFreeSoftwareFoundation;version2oftheLicense.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,but+*WITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.SeetheGNU+*GeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicensealong+*withthisprogram;ifnot,writetotheFreeSoftwareFoundation,Inc.,+*51FranklinStreet,FifthFloor,Boston,MA02110-1301USA.+*/++#include <linux/dma-mapping.h>+#include <linux/interrupt.h>+#include <linux/miscdevice.h>++#include <asm/uaccess.h>+#include <asm/ps3stor.h>+++#define DEVICE_NAME "ps3flash"++#define FLASH_BLOCK_SIZE (256*1024)+++structps3flash_private{+structmutexmutex;+};+#define ps3flash_priv(dev) ((dev)->sbd.core.driver_data)++staticstructps3_storage_device*ps3flash_dev;++staticssize_tps3flash_read_write_sectors(structps3_storage_device*dev,+u64lpar,u64start_sector,+u64sectors,intwrite)+{+constchar*op=write?"write":"read";+u64res=ps3stor_read_write_sectors(dev,lpar,start_sector,sectors,+write);+if(res){+dev_err(&dev->sbd.core,"%s:%u: %s failed 0x%lx\n",__func__,+__LINE__,op,res);+return-EIO;+}+returnsectors;+}++staticssize_tps3flash_read_sectors(structps3_storage_device*dev,+u64start_sector,u64sectors,+unsignedintsector_offset)+{+u64max_sectors,lpar;++max_sectors=dev->bounce_size/dev->blk_size;+if(sectors>max_sectors){+dev_dbg(&dev->sbd.core,"%s:%u Limiting sectors to %lu\n",+__func__,__LINE__,max_sectors);+sectors=max_sectors;+}++lpar=dev->bounce_lpar+sector_offset*dev->blk_size;+returnps3flash_read_write_sectors(dev,lpar,start_sector,sectors,+0);+}++staticssize_tps3flash_write_chunk(structps3_storage_device*dev,+u64start_sector)+{+u64sectors=dev->bounce_size/dev->blk_size;+returnps3flash_read_write_sectors(dev,dev->bounce_lpar,start_sector,+sectors,1);+}++staticloff_tps3flash_llseek(structfile*file,loff_toffset,intorigin)+{+structps3_storage_device*dev=ps3flash_dev;+u64size=dev->regions[dev->region_idx].size*dev->blk_size;++switch(origin){+case1:+offset+=file->f_pos;+break;+case2:+offset+=size;+break;+}+if(offset<0)+return-EINVAL;++file->f_pos=offset;+returnfile->f_pos;+}++staticssize_tps3flash_read(structfile*file,char__user*buf,size_tcount,+loff_t*pos)+{+structps3_storage_device*dev=ps3flash_dev;+structps3flash_private*priv=ps3flash_priv(dev);+u64size,start_sector,end_sector,offset;+ssize_tsectors_read;+size_tremaining,n;++dev_dbg(&dev->sbd.core,+"%s:%u: Reading %zu bytes at position %lld to user 0x%p\n",+__func__,__LINE__,count,*pos,buf);++size=dev->regions[dev->region_idx].size*dev->blk_size;+if(*pos>=size||!count)+return0;++if(*pos+count>size){+dev_dbg(&dev->sbd.core,+"%s:%u Truncating count from %zu to %llu\n",__func__,+__LINE__,count,size-*pos);+count=size-*pos;+}++start_sector=do_div_llr(*pos,dev->blk_size,&offset);+end_sector=DIV_ROUND_UP(*pos+count,dev->blk_size);++remaining=count;+do{+mutex_lock(&priv->mutex);++sectors_read=ps3flash_read_sectors(dev,start_sector,+end_sector-start_sector,+0);+if(sectors_read<0){+mutex_unlock(&priv->mutex);+returnsectors_read;+}++n=min(remaining,sectors_read*dev->blk_size-offset);+dev_dbg(&dev->sbd.core,+"%s:%u: copy %lu bytes from 0x%p to user 0x%p\n",+__func__,__LINE__,n,dev->bounce_buf+offset,buf);+if(copy_to_user(buf,dev->bounce_buf+offset,n)){+mutex_unlock(&priv->mutex);+return-EFAULT;+}++mutex_unlock(&priv->mutex);++*pos+=n;+buf+=n;+remaining-=n;+start_sector+=sectors_read;+offset=0;+}while(remaining>0);++returncount;+}++staticssize_tps3flash_write(structfile*file,constchar__user*buf,+size_tcount,loff_t*pos)+{+structps3_storage_device*dev=ps3flash_dev;+structps3flash_private*priv=ps3flash_priv(dev);+u64size,chunk_sectors,start_write_sector,end_write_sector,+end_read_sector,start_read_sector,head,tail,offset;+ssize_tres;+size_tremaining,n;++dev_dbg(&dev->sbd.core,+"%s:%u: Writing %zu bytes at position %lld from user 0x%p\n",+__func__,__LINE__,count,*pos,buf);++size=dev->regions[dev->region_idx].size*dev->blk_size;+if(*pos>=size||!count)+return0;++if(*pos+count>size){+dev_dbg(&dev->sbd.core,+"%s:%u Truncating count from %zu to %llu\n",__func__,+__LINE__,count,size-*pos);+count=size-*pos;+}++chunk_sectors=dev->bounce_size/dev->blk_size;++start_write_sector=do_div_llr(*pos,dev->bounce_size,&offset)*+chunk_sectors;+end_write_sector=DIV_ROUND_UP(*pos+count,dev->bounce_size)*+chunk_sectors;++end_read_sector=DIV_ROUND_UP(*pos,dev->blk_size);+start_read_sector=(*pos+count)/dev->blk_size;++/*+*Aswehavetowritein256KiBchunks,whilewecanreadinblk_size+*(usually512bytes)chunks,weperformthefollowingsteps:+*1.Readfromstart_write_sectortoend_read_sector("head")+*2.Readfromstart_read_sectortoend_write_sector("tail")+*3.Copydatatobuffer+*4.Writefromstart_write_sectortoend_write_sector+*Allofthisiscomplicatedbyusingonlyone256KiBbouncebuffer.+*/++head=end_read_sector-start_write_sector;+tail=end_write_sector-start_read_sector;++remaining=count;+do{+mutex_lock(&priv->mutex);++if(end_read_sector>=start_read_sector){+/*Mergeheadandtail*/+dev_dbg(&dev->sbd.core,+"Merged head and tail: %lu sectors at %lu\n",+chunk_sectors,start_write_sector);+res=ps3flash_read_sectors(dev,start_write_sector,+chunk_sectors,0);+if(res<0)+gotofail;+}else{+if(head){+/*Readhead*/+dev_dbg(&dev->sbd.core,"head: %lu sectors at %lu\n",+head,start_write_sector);+res=ps3flash_read_sectors(dev,+start_write_sector,+head,0);+if(res<0)+gotofail;+}+if(start_read_sector<+start_write_sector+chunk_sectors){+/*Readtail*/+dev_dbg(&dev->sbd.core,+"tail: %lu sectors at %lu\n",tail,+start_read_sector-start_write_sector);+res=ps3flash_read_sectors(dev,+start_read_sector,+tail,+start_read_sector-start_write_sector);+if(res<0)+gotofail;+}+}++n=min(remaining,dev->bounce_size-offset);+dev_dbg(&dev->sbd.core,+"%s:%u: copy %lu bytes from user 0x%p to 0x%p\n",+__func__,__LINE__,n,buf,dev->bounce_buf+offset);+if(copy_from_user(dev->bounce_buf+offset,buf,n)){+res=-EFAULT;+gotofail;+}++res=ps3flash_write_chunk(dev,start_write_sector);+if(res<0)+gotofail;++mutex_unlock(&priv->mutex);++*pos+=n;+buf+=n;+remaining-=n;+start_write_sector+=chunk_sectors;+head=0;+offset=0;+}while(remaining>0);++returncount;++fail:+mutex_unlock(&priv->mutex);+returnres;+}+++staticconststructfile_operationsps3flash_fops={+.owner=THIS_MODULE,+.llseek=ps3flash_llseek,+.read=ps3flash_read,+.write=ps3flash_write,+};++staticstructmiscdeviceps3flash_misc={+.minor=MISC_DYNAMIC_MINOR,+.name=DEVICE_NAME,+.fops=&ps3flash_fops,+};++staticint__devinitps3flash_probe(structps3_system_bus_device*_dev)+{+structps3_storage_device*dev=to_ps3_storage_device(&_dev->core);+structps3flash_private*priv;+interror;+unsignedlongtmp;++tmp=dev->regions[dev->region_idx].start*dev->blk_size;+if(tmp%FLASH_BLOCK_SIZE){+dev_err(&dev->sbd.core,+"%s:%u region start %lu is not aligned\n",__func__,+__LINE__,tmp);+return-EINVAL;+}+tmp=dev->regions[dev->region_idx].size*dev->blk_size;+if(tmp%FLASH_BLOCK_SIZE){+dev_err(&dev->sbd.core,+"%s:%u region size %lu is not aligned\n",__func__,+__LINE__,tmp);+return-EINVAL;+}++/*usestaticbuffer,kmalloccannotallocate256KiB*/+if(!ps3flash_bounce_buffer.address)+return-ENOMEM;++if(ps3flash_dev){+dev_err(&dev->sbd.core,+"Only one FLASH device is supported\n");+return-EBUSY;+}++ps3flash_dev=dev;++priv=kzalloc(sizeof(*priv),GFP_KERNEL);+if(!priv){+error=-ENOMEM;+gotofail;+}++ps3flash_priv(dev)=priv;+mutex_init(&priv->mutex);++dev->bounce_size=ps3flash_bounce_buffer.size;+dev->bounce_buf=ps3flash_bounce_buffer.address;++error=ps3stor_setup(dev,DEVICE_NAME);+if(error)+gotofail_free_priv;++error=misc_register(&ps3flash_misc);+if(error){+dev_err(&dev->sbd.core,"%s:%u: misc_register failed %d\n",+__func__,__LINE__,error);+gotofail_teardown;+}++dev_info(&dev->sbd.core,"%s:%u: registered misc device %d\n",+__func__,__LINE__,ps3flash_misc.minor);+return0;++fail_teardown:+ps3stor_teardown(dev);+fail_free_priv:+kfree(priv);+fail:+ps3flash_dev=NULL;+returnerror;+}++staticintps3flash_remove(structps3_system_bus_device*_dev)+{+structps3_storage_device*dev=to_ps3_storage_device(&_dev->core);++misc_deregister(&ps3flash_misc);+ps3stor_teardown(dev);+kfree(ps3flash_priv(dev));+ps3flash_dev=NULL;+return0;+}+++staticstructps3_system_bus_driverps3flash={+.match_id=PS3_MATCH_ID_STOR_FLASH,+.core.name=DEVICE_NAME,+.core.owner=THIS_MODULE,+.probe=ps3flash_probe,+.remove=ps3flash_remove,+.shutdown=ps3flash_remove,+};+++staticint__initps3flash_init(void)+{+returnps3_system_bus_driver_register(&ps3flash,PS3_IOBUS_SB);+}++staticvoid__exitps3flash_exit(void)+{+ps3_system_bus_driver_unregister(&ps3flash);+}++module_init(ps3flash_init);+module_exit(ps3flash_exit);++MODULE_LICENSE("GPL");+MODULE_DESCRIPTION("PS3FLASHROMStorageDriver");+MODULE_AUTHOR("SonyCorporation");
--
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
Add a Disk Storage Driver for the PS3:
- Implemented as a block device driver with a dynamic major
- Disk names (and partitions) are of the format ps3d%c(%u)
- Uses software scatter-gather with a 64 KiB bounce buffer as the hypervisor
doesn't support scatter-gather
Signed-off-by: Geert Uytterhoeven <redacted>
---
arch/powerpc/platforms/ps3/Kconfig | 11 +
drivers/block/Makefile | 1
drivers/block/ps3disk.c | 402 +++++++++++++++++++++++++++++++++++++
3 files changed, 414 insertions(+)
@@ -0,0 +1,402 @@+/*+*PS3DiskStorageDriver+*+*Copyright(C)2007SonyComputerEntertainmentInc.+*Copyright2007SonyCorp.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseaspublished+*bytheFreeSoftwareFoundation;version2oftheLicense.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,but+*WITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.SeetheGNU+*GeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicensealong+*withthisprogram;ifnot,writetotheFreeSoftwareFoundation,Inc.,+*51FranklinStreet,FifthFloor,Boston,MA02110-1301USA.+*/++#include <linux/dma-mapping.h>+#include <linux/blkdev.h>+#include <linux/freezer.h>+#include <linux/hdreg.h>+#include <linux/interrupt.h>+#include <linux/kthread.h>++#include <asm/ps3stor.h>+++#define DEVICE_NAME "ps3disk"++#define BOUNCE_SIZE (64*1024)++//FIXMEUseafixedmajorassignedbyLANANA?+#define PS3DISK_MAJOR 0++#define PS3DISK_MAX_DISKS 16+#define PS3DISK_MINORS 16++#define KERNEL_SECTOR_SIZE 512+++#define PS3DISK_NAME "ps3d%c"++#define LV1_STORAGE_ATA_HDDOUT (0x23)+++structps3disk_private{+spinlock_tlock;+structtask_struct*thread;+structrequest_queue*queue;+structgendisk*gendisk;+unsignedintblocking_factor;+};+#define ps3disk_priv(dev) ((dev)->sbd.core.driver_data)++staticintps3disk_major=PS3DISK_MAJOR;++staticintps3disk_open(structinode*inode,structfile*file)+{+structps3_storage_device*dev=inode->i_bdev->bd_disk->private_data;++file->private_data=dev;+return0;+}++++staticstructblock_device_operationsps3disk_fops={+.owner=THIS_MODULE,+.open=ps3disk_open,+};++staticvoidps3disk_scatter_gather(structps3_storage_device*dev,+structrequest*req,intgather)+{+unsignedintsectors=0,offset=0;+structbio*bio;+sector_tsector;+structbio_vec*bvec;+unsignedinti=0,j;+size_tsize;+void*buf;++rq_for_each_bio(bio,req){+sector=bio->bi_sector;+dev_dbg(&dev->sbd.core,+"%s:%u: bio %u: %u segs %u sectors from %lu\n",+__func__,__LINE__,i,bio_segments(bio),+bio_sectors(bio),sector);+bio_for_each_segment(bvec,bio,j){+size=bio_cur_sectors(bio)*KERNEL_SECTOR_SIZE;+buf=__bio_kmap_atomic(bio,j,KM_USER0);+if(gather)+memcpy(dev->bounce_buf+offset,buf,size);+else+memcpy(buf,dev->bounce_buf+offset,size);+offset+=size;+__bio_kunmap_atomic(bio,KM_USER0);+}+sectors+=bio_sectors(bio);+i++;+}+}++staticvoidps3disk_handle_request_sg(structps3_storage_device*dev,+structrequest*req)+{+structps3disk_private*priv=ps3disk_priv(dev);+intuptodate=1;+intwrite=rq_data_dir(req);+constchar*op=write?"write":"read";+u64res;++#ifdef DEBUG+unsignedintn=0;+structbio*bio;+rq_for_each_bio(bio,req)+n++;+dev_dbg(&dev->sbd.core,+"%s:%u: %s req has %u bios for %lu sectors %lu hard sectors\n",+__func__,__LINE__,op,n,req->nr_sectors,+req->hard_nr_sectors);+#endif++if(write)+ps3disk_scatter_gather(dev,req,1);++res=ps3stor_read_write_sectors(dev,dev->bounce_lpar,+req->sector*priv->blocking_factor,+req->nr_sectors*priv->blocking_factor,+write);+if(res){+dev_err(&dev->sbd.core,"%s:%u: %s failed 0x%lx\n",__func__,+__LINE__,op,res);+uptodate=0;+}elseif(!write)+ps3disk_scatter_gather(dev,req,0);++spin_lock_irq(&priv->lock);+if(!end_that_request_first(req,uptodate,req->nr_sectors)){+blkdev_dequeue_request(req);+end_that_request_last(req,uptodate);+}+spin_unlock_irq(&priv->lock);+}++staticintps3disk_thread(void*data)+{+structps3_storage_device*dev=data;+structps3disk_private*priv=ps3disk_priv(dev);+request_queue_t*q=priv->queue;+structrequest*req;++dev_dbg(&dev->sbd.core,"%s thread init\n",__func__);++current->flags|=PF_NOFREEZE;++while(!kthread_should_stop()){+spin_lock_irq(&priv->lock);+set_current_state(TASK_INTERRUPTIBLE);+req=elv_next_request(q);+if(!req){+spin_unlock_irq(&priv->lock);+schedule();+continue;+}+if(!blk_fs_request(req)){+blk_dump_rq_flags(req,DEVICE_NAME" bad request");+end_request(req,0);+spin_unlock_irq(&priv->lock);+continue;+}+spin_unlock_irq(&priv->lock);+ps3disk_handle_request_sg(dev,req);+}++dev_dbg(&dev->sbd.core,"%s thread exit\n",__func__);+return0;+}++staticintps3disk_sync_cache(structps3_storage_device*dev)+{+intres;++dev_dbg(&dev->sbd.core,"%s:%u: sync cache\n",__func__,__LINE__);++res=ps3stor_send_command(dev,LV1_STORAGE_ATA_HDDOUT,0,0,0,0);+if(res){+dev_err(&dev->sbd.core,"%s:%u: sync cache failed 0x%lx\n",+__func__,__LINE__,dev->lv1_status);+return-EIO;+}+return0;+}++staticintps3disk_issue_flush(request_queue_t*q,structgendisk*gendisk,+sector_t*sector)+{+structps3_storage_device*dev=q->queuedata;+returnps3disk_sync_cache(dev);+}++staticvoidps3disk_prepare_flush(request_queue_t*q,structrequest*req)+{+//FIXMEIsthisthecorrectthingtodo?+structps3_storage_device*dev=q->queuedata;+ps3disk_sync_cache(dev);+memset(req->cmd,0,sizeof(req->cmd));+req->cmd_type=REQ_TYPE_FLUSH;+}++staticvoidps3disk_request(request_queue_t*q)+{+structps3_storage_device*dev=q->queuedata;+structps3disk_private*priv=ps3disk_priv(dev);+wake_up_process(priv->thread);+}++staticunsignedlongps3disk_mask;++staticint__devinitps3disk_probe(structps3_system_bus_device*_dev)+{+structps3_storage_device*dev=to_ps3_storage_device(&_dev->core);+structps3disk_private*priv;+interror;+unsignedintdevidx;+structrequest_queue*queue;+structgendisk*gendisk;+structtask_struct*task;++if(dev->blk_size<KERNEL_SECTOR_SIZE){+dev_err(&dev->sbd.core,+"%s:%u: cannot handle block size %lu\n",__func__,+__LINE__,dev->blk_size);+return-EINVAL;+}++BUILD_BUG_ON(PS3DISK_MAX_DISKS>BITS_PER_LONG);+devidx=find_first_zero_bit(&ps3disk_mask,PS3DISK_MAX_DISKS);+if(devidx>=PS3DISK_MAX_DISKS){+dev_err(&dev->sbd.core,"%s:%u: Too many disks\n",__func__,+__LINE__);+return-ENOSPC;+}+__set_bit(devidx,&ps3disk_mask);++priv=kzalloc(sizeof(*priv),GFP_KERNEL);+if(!priv){+error=-ENOMEM;+gotofail;+}++ps3disk_priv(dev)=priv;+spin_lock_init(&priv->lock);++dev->bounce_size=BOUNCE_SIZE;+dev->bounce_buf=kmalloc(BOUNCE_SIZE,GFP_DMA);+if(!dev->bounce_buf){+error=-ENOMEM;+gotofail_free_priv;+}++error=ps3stor_setup(dev,DEVICE_NAME);+if(error)+gotofail_free_bounce;++queue=blk_init_queue(ps3disk_request,&priv->lock);+if(!queue){+dev_err(&dev->sbd.core,"%s:%u: blk_init_queue failed\n",+__func__,__LINE__);+error=-ENOMEM;+gotofail_teardown;+}++priv->queue=queue;+queue->queuedata=dev;++blk_queue_bounce_limit(queue,BLK_BOUNCE_HIGH);++blk_queue_max_sectors(queue,dev->bounce_size/KERNEL_SECTOR_SIZE);+blk_queue_segment_boundary(queue,-1UL);+blk_queue_dma_alignment(queue,dev->blk_size-1);+blk_queue_hardsect_size(queue,dev->blk_size);++blk_queue_issue_flush_fn(queue,ps3disk_issue_flush);+blk_queue_ordered(queue,QUEUE_ORDERED_DRAIN_FLUSH,+ps3disk_prepare_flush);++blk_queue_max_phys_segments(queue,-1);+blk_queue_max_hw_segments(queue,-1);+blk_queue_max_segment_size(queue,dev->bounce_size);++gendisk=alloc_disk(PS3DISK_MINORS);+if(!gendisk){+dev_err(&dev->sbd.core,"%s:%u: alloc_disk failed\n",__func__,+__LINE__);+error=-ENOMEM;+gotofail_cleanup_queue;+}++priv->gendisk=gendisk;+gendisk->major=ps3disk_major;+gendisk->first_minor=devidx*PS3DISK_MINORS;+gendisk->fops=&ps3disk_fops;+gendisk->queue=queue;+gendisk->private_data=dev;+snprintf(gendisk->disk_name,sizeof(gendisk->disk_name),PS3DISK_NAME,+devidx+'a');+priv->blocking_factor=dev->blk_size/KERNEL_SECTOR_SIZE;+set_capacity(gendisk,+dev->regions[dev->region_idx].size*priv->blocking_factor);++task=kthread_run(ps3disk_thread,dev,DEVICE_NAME);+if(IS_ERR(task)){+error=PTR_ERR(task);+gotofail_free_disk;+}+priv->thread=task;++add_disk(gendisk);+return0;++fail_free_disk:+put_disk(priv->gendisk);+fail_cleanup_queue:+blk_cleanup_queue(queue);+fail_teardown:+ps3stor_teardown(dev);+fail_free_bounce:+kfree(dev->bounce_buf);+fail_free_priv:+kfree(priv);+fail:+__clear_bit(devidx,&ps3disk_mask);+returnerror;+}++staticintps3disk_remove(structps3_system_bus_device*_dev)+{+structps3_storage_device*dev=to_ps3_storage_device(&_dev->core);+structps3disk_private*priv=ps3disk_priv(dev);++kthread_stop(priv->thread);+__clear_bit(priv->gendisk->first_minor/PS3DISK_MINORS,+&ps3disk_mask);+del_gendisk(priv->gendisk);+put_disk(priv->gendisk);+blk_cleanup_queue(priv->queue);+dev_notice(&dev->sbd.core,"Synchronizing disk cache\n");+ps3disk_sync_cache(dev);+ps3stor_teardown(dev);+kfree(dev->bounce_buf);+kfree(priv);+return0;+}+++staticstructps3_system_bus_driverps3disk={+.match_id=PS3_MATCH_ID_STOR_DISK,+.core.name=DEVICE_NAME,+.core.owner=THIS_MODULE,+.probe=ps3disk_probe,+.remove=ps3disk_remove,+.shutdown=ps3disk_remove,+};+++staticint__initps3disk_init(void)+{+interror;++error=register_blkdev(ps3disk_major,DEVICE_NAME);+if(error<=0){+printk(KERN_ERR"%s:%u: register_blkdev failed %d\n",__func__,+__LINE__,error);+returnerror;+}+if(!ps3disk_major)+ps3disk_major=error;++pr_info("%s:%u:registeredblockdevicemajor%d\n",__func__,+__LINE__,ps3disk_major);++returnps3_system_bus_driver_register(&ps3disk,PS3_IOBUS_SB);+}++staticvoid__exitps3disk_exit(void)+{+unregister_blkdev(ps3disk_major,DEVICE_NAME);++ps3_system_bus_driver_unregister(&ps3disk);+}++module_init(ps3disk_init);+module_exit(ps3disk_exit);++MODULE_LICENSE("GPL");+MODULE_DESCRIPTION("PS3DiskStorageDriver");+MODULE_AUTHOR("SonyCorporation");+
--
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
@@ -0,0 +1,816 @@+/*+*PS3ROMStorageDriver+*+*Copyright(C)2007SonyComputerEntertainmentInc.+*Copyright2007SonyCorp.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseaspublished+*bytheFreeSoftwareFoundation;version2oftheLicense.+*+*Thisprogramisdistributedinthehopethatitwillbeuseful,but+*WITHOUTANYWARRANTY;withouteventheimpliedwarrantyof+*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.SeetheGNU+*GeneralPublicLicenseformoredetails.+*+*YoushouldhavereceivedacopyoftheGNUGeneralPublicLicensealong+*withthisprogram;ifnot,writetotheFreeSoftwareFoundation,Inc.,+*51FranklinStreet,FifthFloor,Boston,MA02110-1301USA.+*/++#include <linux/cdrom.h>+#include <linux/interrupt.h>+#include <linux/kthread.h>++#include <scsi/scsi.h>+#include <scsi/scsi_cmnd.h>+#include <scsi/scsi_device.h>+#include <scsi/scsi_host.h>++#include <asm/ps3stor.h>+++#define DEVICE_NAME "ps3rom"++#define BOUNCE_SIZE (64*1024)++#define PS3ROM_MAX_SECTORS (BOUNCE_SIZE / CD_FRAMESIZE)++#define LV1_STORAGE_SEND_ATAPI_COMMAND (1)+++structps3rom_private{+spinlock_tlock;+structtask_struct*thread;+structScsi_Host*host;+structscsi_cmnd*cmd;+void(*scsi_done)(structscsi_cmnd*);+};+#define ps3rom_priv(dev) ((dev)->sbd.core.driver_data)++structlv1_atapi_cmnd_block{+u8pkt[32];/*packetcommandblock*/+u32pktlen;/*shouldbe12forATAPI8020*/+u32blocks;+u32block_size;+u32proto;/*transfermode*/+u32in_out;/*transferdirection*/+u64buffer;/*parameterexceptcommandblock*/+u32arglen;/*lengthabove*/+};++/*+*topositionparameter+*/+enum{+NOT_AVAIL=-1,+USE_SRB_10=-2,+USE_SRB_6=-3,+USE_CDDA_FRAME_RAW=-4+};++enumlv1_atapi_proto{+NA_PROTO=-1,+NON_DATA_PROTO=0,+PIO_DATA_IN_PROTO=1,+PIO_DATA_OUT_PROTO=2,+DMA_PROTO=3+};++enumlv1_atapi_in_out{+DIR_NA=-1,+DIR_WRITE=0,/*memory->device*/+DIR_READ=1/*device->memory*/+};+++#ifdef DEBUG+staticconstchar*scsi_command(unsignedcharcmd)+{+switch(cmd){+caseTEST_UNIT_READY:return"TEST_UNIT_READY/GPCMD_TEST_UNIT_READY";+caseREZERO_UNIT:return"REZERO_UNIT";+caseREQUEST_SENSE:return"REQUEST_SENSE/GPCMD_REQUEST_SENSE";+caseFORMAT_UNIT:return"FORMAT_UNIT/GPCMD_FORMAT_UNIT";+caseREAD_BLOCK_LIMITS:return"READ_BLOCK_LIMITS";+caseREASSIGN_BLOCKS:return"REASSIGN_BLOCKS/INITIALIZE_ELEMENT_STATUS";+caseREAD_6:return"READ_6";+caseWRITE_6:return"WRITE_6/MI_REPORT_TARGET_PGS";+caseSEEK_6:return"SEEK_6";+caseREAD_REVERSE:return"READ_REVERSE";+caseWRITE_FILEMARKS:return"WRITE_FILEMARKS/SAI_READ_CAPACITY_16";+caseSPACE:return"SPACE";+caseINQUIRY:return"INQUIRY/GPCMD_INQUIRY";+caseRECOVER_BUFFERED_DATA:return"RECOVER_BUFFERED_DATA";+caseMODE_SELECT:return"MODE_SELECT";+caseRESERVE:return"RESERVE";+caseRELEASE:return"RELEASE";+caseCOPY:return"COPY";+caseERASE:return"ERASE";+caseMODE_SENSE:return"MODE_SENSE";+caseSTART_STOP:return"START_STOP/GPCMD_START_STOP_UNIT";+caseRECEIVE_DIAGNOSTIC:return"RECEIVE_DIAGNOSTIC";+caseSEND_DIAGNOSTIC:return"SEND_DIAGNOSTIC";+caseALLOW_MEDIUM_REMOVAL:return"ALLOW_MEDIUM_REMOVAL/GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL";+caseSET_WINDOW:return"SET_WINDOW";+caseREAD_CAPACITY:return"READ_CAPACITY/GPCMD_READ_CDVD_CAPACITY";+caseREAD_10:return"READ_10/GPCMD_READ_10";+caseWRITE_10:return"WRITE_10/GPCMD_WRITE_10";+caseSEEK_10:return"SEEK_10/POSITION_TO_ELEMENT/GPCMD_SEEK";+caseWRITE_VERIFY:return"WRITE_VERIFY/GPCMD_WRITE_AND_VERIFY_10";+caseVERIFY:return"VERIFY/GPCMD_VERIFY_10";+caseSEARCH_HIGH:return"SEARCH_HIGH";+caseSEARCH_EQUAL:return"SEARCH_EQUAL";+caseSEARCH_LOW:return"SEARCH_LOW";+caseSET_LIMITS:return"SET_LIMITS";+casePRE_FETCH:return"PRE_FETCH/READ_POSITION";+caseSYNCHRONIZE_CACHE:return"SYNCHRONIZE_CACHE/GPCMD_FLUSH_CACHE";+caseLOCK_UNLOCK_CACHE:return"LOCK_UNLOCK_CACHE";+caseREAD_DEFECT_DATA:return"READ_DEFECT_DATA";+caseMEDIUM_SCAN:return"MEDIUM_SCAN";+caseCOMPARE:return"COMPARE";+caseCOPY_VERIFY:return"COPY_VERIFY";+caseWRITE_BUFFER:return"WRITE_BUFFER";+caseREAD_BUFFER:return"READ_BUFFER";+caseUPDATE_BLOCK:return"UPDATE_BLOCK";+caseREAD_LONG:return"READ_LONG";+caseWRITE_LONG:return"WRITE_LONG";+caseCHANGE_DEFINITION:return"CHANGE_DEFINITION";+caseWRITE_SAME:return"WRITE_SAME";+caseREAD_TOC:return"READ_TOC/GPCMD_READ_TOC_PMA_ATIP";+caseLOG_SELECT:return"LOG_SELECT";+caseLOG_SENSE:return"LOG_SENSE";+caseMODE_SELECT_10:return"MODE_SELECT_10/GPCMD_MODE_SELECT_10";+caseRESERVE_10:return"RESERVE_10";+caseRELEASE_10:return"RELEASE_10";+caseMODE_SENSE_10:return"MODE_SENSE_10/GPCMD_MODE_SENSE_10";+casePERSISTENT_RESERVE_IN:return"PERSISTENT_RESERVE_IN";+casePERSISTENT_RESERVE_OUT:return"PERSISTENT_RESERVE_OUT";+caseREPORT_LUNS:return"REPORT_LUNS";+caseMAINTENANCE_IN:return"MAINTENANCE_IN/GPCMD_SEND_KEY";+caseMOVE_MEDIUM:return"MOVE_MEDIUM";+caseEXCHANGE_MEDIUM:return"EXCHANGE_MEDIUM/GPCMD_LOAD_UNLOAD";+caseREAD_12:return"READ_12/GPCMD_READ_12";+caseWRITE_12:return"WRITE_12";+caseWRITE_VERIFY_12:return"WRITE_VERIFY_12";+caseSEARCH_HIGH_12:return"SEARCH_HIGH_12";+caseSEARCH_EQUAL_12:return"SEARCH_EQUAL_12";+caseSEARCH_LOW_12:return"SEARCH_LOW_12";+caseREAD_ELEMENT_STATUS:return"READ_ELEMENT_STATUS";+caseSEND_VOLUME_TAG:return"SEND_VOLUME_TAG/GPCMD_SET_STREAMING";+caseWRITE_LONG_2:return"WRITE_LONG_2";+caseREAD_16:return"READ_16";+caseWRITE_16:return"WRITE_16";+caseVERIFY_16:return"VERIFY_16";+caseSERVICE_ACTION_IN:return"SERVICE_ACTION_IN";+caseATA_16:return"ATA_16";+caseATA_12:return"ATA_12/GPCMD_BLANK";+caseGPCMD_CLOSE_TRACK:return"GPCMD_CLOSE_TRACK";+caseGPCMD_GET_CONFIGURATION:return"GPCMD_GET_CONFIGURATION";+caseGPCMD_GET_EVENT_STATUS_NOTIFICATION:return"GPCMD_GET_EVENT_STATUS_NOTIFICATION";+caseGPCMD_GET_PERFORMANCE:return"GPCMD_GET_PERFORMANCE";+caseGPCMD_MECHANISM_STATUS:return"GPCMD_MECHANISM_STATUS";+caseGPCMD_PAUSE_RESUME:return"GPCMD_PAUSE_RESUME";+caseGPCMD_PLAY_AUDIO_10:return"GPCMD_PLAY_AUDIO_10";+caseGPCMD_PLAY_AUDIO_MSF:return"GPCMD_PLAY_AUDIO_MSF";+caseGPCMD_PLAY_AUDIO_TI:return"GPCMD_PLAY_AUDIO_TI/GPCMD_PLAYAUDIO_TI";+caseGPCMD_PLAY_CD:return"GPCMD_PLAY_CD";+caseGPCMD_READ_BUFFER_CAPACITY:return"GPCMD_READ_BUFFER_CAPACITY";+caseGPCMD_READ_CD:return"GPCMD_READ_CD";+caseGPCMD_READ_CD_MSF:return"GPCMD_READ_CD_MSF";+caseGPCMD_READ_DISC_INFO:return"GPCMD_READ_DISC_INFO";+caseGPCMD_READ_DVD_STRUCTURE:return"GPCMD_READ_DVD_STRUCTURE";+caseGPCMD_READ_FORMAT_CAPACITIES:return"GPCMD_READ_FORMAT_CAPACITIES";+caseGPCMD_READ_HEADER:return"GPCMD_READ_HEADER";+caseGPCMD_READ_TRACK_RZONE_INFO:return"GPCMD_READ_TRACK_RZONE_INFO";+caseGPCMD_READ_SUBCHANNEL:return"GPCMD_READ_SUBCHANNEL";+caseGPCMD_REPAIR_RZONE_TRACK:return"GPCMD_REPAIR_RZONE_TRACK";+caseGPCMD_REPORT_KEY:return"GPCMD_REPORT_KEY";+caseGPCMD_RESERVE_RZONE_TRACK:return"GPCMD_RESERVE_RZONE_TRACK";+caseGPCMD_SEND_CUE_SHEET:return"GPCMD_SEND_CUE_SHEET";+caseGPCMD_SCAN:return"GPCMD_SCAN";+caseGPCMD_SEND_DVD_STRUCTURE:return"GPCMD_SEND_DVD_STRUCTURE";+caseGPCMD_SEND_EVENT:return"GPCMD_SEND_EVENT";+caseGPCMD_SEND_OPC:return"GPCMD_SEND_OPC";+caseGPCMD_SET_READ_AHEAD:return"GPCMD_SET_READ_AHEAD";+caseGPCMD_STOP_PLAY_SCAN:return"GPCMD_STOP_PLAY_SCAN";+caseGPCMD_SET_SPEED:return"GPCMD_SET_SPEED";+caseGPCMD_GET_MEDIA_STATUS:return"GPCMD_GET_MEDIA_STATUS";++default:+return"***UNKNOWN***";+}+}+#else /* !DEBUG */+staticinlineconstchar*scsi_command(unsignedcharcmd){returnNULL;}+#endif /* DEBUG */+++staticintps3rom_slave_alloc(structscsi_device*scsi_dev)+{+structps3_storage_device*dev;++dev=(structps3_storage_device*)scsi_dev->host->hostdata[0];++dev_dbg(&dev->sbd.core,"%s:%u: id %u, lun %u, channel %u\n",__func__,+__LINE__,scsi_dev->id,scsi_dev->lun,scsi_dev->channel);++scsi_dev->hostdata=dev;+return0;+}++staticintps3rom_slave_configure(structscsi_device*scsi_dev)+{+structps3_storage_device*dev=scsi_dev->hostdata;++dev_dbg(&dev->sbd.core,"%s:%u: id %u, lun %u, channel %u\n",__func__,+__LINE__,scsi_dev->id,scsi_dev->lun,scsi_dev->channel);++/*+*ATAPISFF8020devicesuseMODE_SENSE_10,+*sowecanprohibitMODE_SENSE_6+*/+scsi_dev->use_10_for_ms=1;++return0;+}++staticvoidps3rom_slave_destroy(structscsi_device*scsi_dev)+{+}++staticintps3rom_queuecommand(structscsi_cmnd*cmd,+void(*done)(structscsi_cmnd*))+{+structps3_storage_device*dev=cmd->device->hostdata;+structps3rom_private*priv=ps3rom_priv(dev);++dev_dbg(&dev->sbd.core,"%s:%u: command 0x%02x (%s)\n",__func__,+__LINE__,cmd->cmnd[0],scsi_command(cmd->cmnd[0]));++spin_lock_irq(&priv->lock);+if(priv->cmd){+/*nomorethanonecanbeprocessed*/+dev_err(&dev->sbd.core,"%s:%u: more than 1 command queued\n",+__func__,__LINE__);+spin_unlock_irq(&priv->lock);+returnSCSI_MLQUEUE_HOST_BUSY;+}++//FIXMEPrevalidatecommands?+priv->cmd=cmd;+priv->scsi_done=done;+spin_unlock_irq(&priv->lock);+wake_up_process(priv->thread);+return0;+}++/*+*copydatafromdeviceintoscatter/gatherbuffer+*/+staticintfill_from_dev_buffer(structscsi_cmnd*cmd,constvoid*buf,+intbuflen)+{+intk,req_len,act_len,len,active;+void*kaddr;+structscatterlist*sgpnt;++if(!cmd->request_bufflen)+return0;++if(!cmd->request_buffer)+returnDID_ERROR<<16;++if(cmd->sc_data_direction!=DMA_BIDIRECTIONAL&&+cmd->sc_data_direction!=DMA_FROM_DEVICE)+returnDID_ERROR<<16;++if(!cmd->use_sg){+req_len=cmd->request_bufflen;+act_len=min(req_len,buflen);+memcpy(cmd->request_buffer,buf,act_len);+cmd->resid=req_len-act_len;+return0;+}++sgpnt=cmd->request_buffer;+active=1;+for(k=0,req_len=0,act_len=0;k<cmd->use_sg;++k,++sgpnt){+if(active){+kaddr=kmap_atomic(sgpnt->page,KM_USER0);+if(!kaddr)+returnDID_ERROR<<16;+len=sgpnt->length;+if((req_len+len)>buflen){+active=0;+len=buflen-req_len;+}+memcpy(kaddr+sgpnt->offset,buf+req_len,len);+kunmap_atomic(kaddr,KM_USER0);+act_len+=len;+}+req_len+=sgpnt->length;+}+cmd->resid=req_len-act_len;+return0;+}++/*+*copydatafromscatter/gatherintodevice'sbuffer+*/+staticintfetch_to_dev_buffer(structscsi_cmnd*cmd,void*buf,intbuflen)+{+intk,req_len,len,fin;+void*kaddr;+structscatterlist*sgpnt;++if(!cmd->request_bufflen)+return0;++if(!cmd->request_buffer)+return-1;++if(cmd->sc_data_direction!=DMA_BIDIRECTIONAL&&+cmd->sc_data_direction!=DMA_TO_DEVICE)+return-1;++if(!cmd->use_sg){+req_len=cmd->request_bufflen;+len=min(req_len,buflen);+memcpy(buf,cmd->request_buffer,len);+returnlen;+}++sgpnt=cmd->request_buffer;+for(k=0,req_len=0,fin=0;k<cmd->use_sg;++k,++sgpnt){+kaddr=kmap_atomic(sgpnt->page,KM_USER0);+if(!kaddr)+return-1;+len=sgpnt->length;+if((req_len+len)>buflen){+len=buflen-req_len;+fin=1;+}+memcpy(buf+req_len,kaddr+sgpnt->offset,len);+kunmap_atomic(kaddr,KM_USER0);+if(fin)+returnreq_len+len;+req_len+=sgpnt->length;+}+returnreq_len;+}++staticintdecode_lv1_status(u64status,unsignedchar*sense_key,+unsignedchar*asc,unsignedchar*ascq)+{+if(((status>>24)&0xff)!=SAM_STAT_CHECK_CONDITION)+return-1;++*sense_key=(status>>16)&0xff;+*asc=(status>>8)&0xff;+*ascq=status&0xff;+return0;+}++staticinlineunsignedintsrb6_lba(conststructscsi_cmnd*cmd)+{+BUG_ON(cmd->cmnd[1]&0xe0);//FIXMElun==0+returncmd->cmnd[1]<<16|cmd->cmnd[2]<<8|cmd->cmnd[3];+}++staticinlineunsignedintsrb6_len(conststructscsi_cmnd*cmd)+{+returncmd->cmnd[4];+}++staticinlineunsignedintsrb10_lba(conststructscsi_cmnd*cmd)+{+returncmd->cmnd[2]<<24|cmd->cmnd[3]<<16|cmd->cmnd[4]<<8|+cmd->cmnd[5];+}++staticinlineunsignedintsrb10_len(conststructscsi_cmnd*cmd)+{+returncmd->cmnd[7]<<8|cmd->cmnd[8];+}++staticinlineunsignedintcdda_raw_len(conststructscsi_cmnd*cmd)+{+unsignedintnframes;++nframes=cmd->cmnd[6]<<16|cmd->cmnd[7]<<8|cmd->cmnd[8];+returnnframes*CD_FRAMESIZE_RAW;+}++staticu64ps3rom_send_atapi_command(structps3_storage_device*dev,+structlv1_atapi_cmnd_block*cmd)+{+dev_dbg(&dev->sbd.core,"%s:%u: send ATAPI command 0x%02x (%s)\n",+__func__,__LINE__,cmd->pkt[0],scsi_command(cmd->pkt[0]));++returnps3stor_send_command(dev,LV1_STORAGE_SEND_ATAPI_COMMAND,+ps3_mm_phys_to_lpar(__pa(cmd)),+sizeof(*cmd),cmd->buffer,cmd->arglen);+}++staticvoidps3rom_atapi_request(structps3_storage_device*dev,+structscsi_cmnd*cmd,unsignedintlen,+intproto,intin_out,intauto_sense)+{+structlv1_atapi_cmnd_blockatapi_cmnd;+unsignedchar*cmnd=cmd->cmnd;+u64status;+unsignedcharsense_key,asc,ascq;++if(len>dev->bounce_size){+staticintprinted;+if(!printed++)+dev_err(&dev->sbd.core,+"%s:%u: data size too large %u > %lu\n",+__func__,__LINE__,len,dev->bounce_size);+cmd->result=DID_ERROR<<16;+memset(cmd->sense_buffer,0,SCSI_SENSE_BUFFERSIZE);+cmd->sense_buffer[0]=0x70;+cmd->sense_buffer[2]=ILLEGAL_REQUEST;+return;+}++memset(&atapi_cmnd,0,sizeof(structlv1_atapi_cmnd_block));+memcpy(&atapi_cmnd.pkt,cmnd,12);+atapi_cmnd.pktlen=12;+atapi_cmnd.proto=proto;+if(in_out!=DIR_NA)+atapi_cmnd.in_out=in_out;++if(atapi_cmnd.in_out==DIR_WRITE){+//FIXMEcheckerror+fetch_to_dev_buffer(cmd,dev->bounce_buf,len);+}++atapi_cmnd.block_size=1;/*transfersizeisblock_size*blocks*/++atapi_cmnd.blocks=atapi_cmnd.arglen=len;+atapi_cmnd.buffer=dev->bounce_lpar;++status=ps3rom_send_atapi_command(dev,&atapi_cmnd);+if(status==-1){+cmd->result=DID_ERROR<<16;/*FIXME:isbetterothererrorcode?*/+return;+}++if(!status){+/*OK,completed*/+if(atapi_cmnd.in_out==DIR_READ){+//FIXMEcheckerror+fill_from_dev_buffer(cmd,dev->bounce_buf,len);+}+cmd->result=DID_OK<<16;+return;+}++/*error*/+if(!auto_sense){+cmd->result=(DID_ERROR<<16)|(CHECK_CONDITION<<1);+dev_err(&dev->sbd.core,"%s:%u: end error without autosense\n",+__func__,__LINE__);+return;+}++if(!decode_lv1_status(status,&sense_key,&asc,&ascq)){+/*lv1mayhaveissuedautosense...*/+cmd->sense_buffer[0]=0x70;+cmd->sense_buffer[2]=sense_key;+cmd->sense_buffer[7]=16-6;+cmd->sense_buffer[12]=asc;+cmd->sense_buffer[13]=ascq;+cmd->result=SAM_STAT_CHECK_CONDITION;+return;+}++/*doautosensebyourselves*/+memset(&atapi_cmnd,0,sizeof(structlv1_atapi_cmnd_block));+atapi_cmnd.pkt[0]=REQUEST_SENSE;+atapi_cmnd.pkt[4]=18;+atapi_cmnd.pktlen=12;+atapi_cmnd.arglen=atapi_cmnd.blocks=atapi_cmnd.pkt[4];+atapi_cmnd.block_size=1;+atapi_cmnd.proto=DMA_PROTO;+atapi_cmnd.in_out=DIR_READ;+atapi_cmnd.buffer=dev->bounce_lpar;++/*issueREQUEST_SENSEcommand*/+status=ps3rom_send_atapi_command(dev,&atapi_cmnd);+if(status==-1){+cmd->result=DID_ERROR<<16;/*FIXME:isbetterothererrorcode?*/+return;+}++/*scsispecsaysrequestsenseshouldnevergeterror*/+if(status){+decode_lv1_status(status,&sense_key,&asc,&ascq);+dev_err(&dev->sbd.core,+"%s:%u: auto REQUEST_SENSE error %#x %#x %#x\n",+__func__,__LINE__,sense_key,asc,ascq);+}++memcpy(cmd->sense_buffer,dev->bounce_buf,+min_t(size_t,atapi_cmnd.pkt[4],SCSI_SENSE_BUFFERSIZE));+cmd->result=SAM_STAT_CHECK_CONDITION;+}++staticvoidps3rom_read_request(structps3_storage_device*dev,+structscsi_cmnd*cmd,u32start_sector,+u32sectors)+{+u64status;++status=ps3stor_read_write_sectors(dev,dev->bounce_lpar,+start_sector,sectors,0);+if(status==-1){+cmd->result=DID_ERROR<<16;/*FIXME:othererrorcode?*/+return;+}++if(status){+memset(cmd->sense_buffer,0,SCSI_SENSE_BUFFERSIZE);+decode_lv1_status(dev->lv1_status,&cmd->sense_buffer[2],+&cmd->sense_buffer[12],+&cmd->sense_buffer[13]);+cmd->sense_buffer[7]=16-6;//FIXMEhardcodednumbers?+cmd->result=SAM_STAT_CHECK_CONDITION;+return;+}++//FIXMEcheckerror+fill_from_dev_buffer(cmd,dev->bounce_buf,sectors*CD_FRAMESIZE);++cmd->result=DID_OK<<16;+}++staticvoidps3rom_write_request(structps3_storage_device*dev,+structscsi_cmnd*cmd,u32start_sector,+u32sectors)+{+u64status;++//FIXMEcheckerror+fetch_to_dev_buffer(cmd,dev->bounce_buf,sectors*CD_FRAMESIZE);++status=ps3stor_read_write_sectors(dev,dev->bounce_lpar,+start_sector,sectors,1);+if(status==-1){+cmd->result=DID_ERROR<<16;/*FIXME:othererrorcode?*/+return;+}++if(status){+memset(cmd->sense_buffer,0,SCSI_SENSE_BUFFERSIZE);+decode_lv1_status(dev->lv1_status,&cmd->sense_buffer[2],+&cmd->sense_buffer[12],+&cmd->sense_buffer[13]);+cmd->sense_buffer[7]=16-6;//FIXMEhardcodednumbers?+cmd->result=SAM_STAT_CHECK_CONDITION;+return;+}++cmd->result=DID_OK<<16;+}++staticvoidps3rom_request(structps3_storage_device*dev,+structscsi_cmnd*cmd)+{+unsignedcharopcode=cmd->cmnd[0];+structps3rom_private*priv=ps3rom_priv(dev);++dev_dbg(&dev->sbd.core,"%s:%u: command 0x%02x (%s)\n",__func__,+__LINE__,opcode,scsi_command(opcode));++switch(opcode){+caseINQUIRY:+ps3rom_atapi_request(dev,cmd,srb6_len(cmd),+PIO_DATA_IN_PROTO,DIR_READ,1);+break;++caseREQUEST_SENSE:+ps3rom_atapi_request(dev,cmd,srb6_len(cmd),+PIO_DATA_IN_PROTO,DIR_READ,0);+break;++caseALLOW_MEDIUM_REMOVAL:+caseSTART_STOP:+caseTEST_UNIT_READY:+ps3rom_atapi_request(dev,cmd,0,NON_DATA_PROTO,DIR_NA,1);+break;++caseREAD_CAPACITY:+ps3rom_atapi_request(dev,cmd,8,PIO_DATA_IN_PROTO,DIR_READ,+1);+break;++caseMODE_SENSE_10:+caseREAD_TOC:+caseGPCMD_GET_CONFIGURATION:+caseGPCMD_READ_DISC_INFO:+ps3rom_atapi_request(dev,cmd,srb10_len(cmd),+PIO_DATA_IN_PROTO,DIR_READ,1);+break;++caseREAD_6:+ps3rom_read_request(dev,cmd,srb6_lba(cmd),srb6_len(cmd));+break;++caseREAD_10:+ps3rom_read_request(dev,cmd,srb10_lba(cmd),srb10_len(cmd));+break;++caseWRITE_6:+ps3rom_write_request(dev,cmd,srb6_lba(cmd),srb6_len(cmd));+break;++caseWRITE_10:+ps3rom_write_request(dev,cmd,srb10_lba(cmd),srb10_len(cmd));+break;++caseGPCMD_READ_CD:+ps3rom_atapi_request(dev,cmd,cdda_raw_len(cmd),DMA_PROTO,+DIR_READ,1);+break;++default:+dev_err(&dev->sbd.core,"%s:%u: illegal request 0x%02x (%s)\n",+__func__,__LINE__,opcode,scsi_command(opcode));+cmd->result=DID_ERROR<<16;+memset(cmd->sense_buffer,0,SCSI_SENSE_BUFFERSIZE);+cmd->sense_buffer[0]=0x70;+cmd->sense_buffer[2]=ILLEGAL_REQUEST;+}++spin_lock_irq(&priv->lock);+priv->cmd=NULL;+priv->scsi_done(cmd);+spin_unlock_irq(&priv->lock);+}++staticintps3rom_thread(void*data)+{+structps3_storage_device*dev=data;+structps3rom_private*priv=ps3rom_priv(dev);+structscsi_cmnd*cmd;++dev_dbg(&dev->sbd.core,"%s thread init\n",__func__);++current->flags|=PF_NOFREEZE;++while(!kthread_should_stop()){+spin_lock_irq(&priv->lock);+set_current_state(TASK_INTERRUPTIBLE);+cmd=priv->cmd;+spin_unlock_irq(&priv->lock);+if(!cmd){+schedule();+continue;+}+ps3rom_request(dev,cmd);+}++dev_dbg(&dev->sbd.core,"%s thread exit\n",__func__);+return0;+}+++staticstructscsi_host_templateps3rom_host_template={+.name=DEVICE_NAME,+.slave_alloc=ps3rom_slave_alloc,+.slave_configure=ps3rom_slave_configure,+.slave_destroy=ps3rom_slave_destroy,+.queuecommand=ps3rom_queuecommand,+.can_queue=1,+.this_id=7,+.sg_tablesize=SG_ALL,+.cmd_per_lun=1,+.emulated=1,/*onlysgdriverusesthis*/+.max_sectors=PS3ROM_MAX_SECTORS,+.use_clustering=ENABLE_CLUSTERING,+.module=THIS_MODULE,+};+++staticint__devinitps3rom_probe(structps3_system_bus_device*_dev)+{+structps3_storage_device*dev=to_ps3_storage_device(&_dev->core);+structps3rom_private*priv;+interror;+structScsi_Host*host;+structtask_struct*task;++if(dev->blk_size!=CD_FRAMESIZE){+dev_err(&dev->sbd.core,+"%s:%u: cannot handle block size %lu\n",__func__,+__LINE__,dev->blk_size);+return-EINVAL;+}++priv=kzalloc(sizeof(*priv),GFP_KERNEL);+if(!priv)+return-ENOMEM;++ps3rom_priv(dev)=priv;+spin_lock_init(&priv->lock);++dev->bounce_size=BOUNCE_SIZE;+dev->bounce_buf=kmalloc(BOUNCE_SIZE,GFP_DMA);+if(!dev->bounce_buf){+error=-ENOMEM;+gotofail_free_priv;+}++error=ps3stor_setup(dev,DEVICE_NAME);+if(error)+gotofail_free_bounce;++host=scsi_host_alloc(&ps3rom_host_template,+sizeof(structps3_system_bus_device*));+if(!host){+dev_err(&dev->sbd.core,"%s:%u: scsi_host_alloc failed\n",+__func__,__LINE__);+gotofail_teardown;+}++priv->host=host;+host->hostdata[0]=(unsignedlong)dev;++/*Onedevice/LUNperSCSIbus*/+host->max_id=1;+host->max_lun=1;++error=scsi_add_host(host,&dev->sbd.core);+if(error){+dev_err(&dev->sbd.core,"%s:%u: scsi_host_alloc failed %d\n",+__func__,__LINE__,error);+error=-ENODEV;+gotofail_host_put;+}++task=kthread_run(ps3rom_thread,dev,DEVICE_NAME);+if(IS_ERR(task)){+error=PTR_ERR(task);+gotofail_remove_host;+}+priv->thread=task;++scsi_scan_host(host);+return0;++fail_remove_host:+scsi_remove_host(host);+fail_host_put:+scsi_host_put(host);+fail_teardown:+ps3stor_teardown(dev);+fail_free_bounce:+kfree(dev->bounce_buf);+fail_free_priv:+kfree(priv);+returnerror;+}++staticintps3rom_remove(structps3_system_bus_device*_dev)+{+structps3_storage_device*dev=to_ps3_storage_device(&_dev->core);+structps3rom_private*priv=ps3rom_priv(dev);++scsi_remove_host(priv->host);+scsi_host_put(priv->host);+kthread_stop(priv->thread);+ps3stor_teardown(dev);+kfree(dev->bounce_buf);+kfree(priv);+return0;+}+++staticstructps3_system_bus_driverps3rom={+.match_id=PS3_MATCH_ID_STOR_ROM,+.core.name=DEVICE_NAME,+.core.owner=THIS_MODULE,+.probe=ps3rom_probe,+.remove=ps3rom_remove+};+++staticint__initps3rom_init(void)+{+returnps3_system_bus_driver_register(&ps3rom,PS3_IOBUS_SB);+}++staticvoid__exitps3rom_exit(void)+{+ps3_system_bus_driver_unregister(&ps3rom);+}++module_init(ps3rom_init);+module_exit(ps3rom_exit);++MODULE_LICENSE("GPL");+MODULE_DESCRIPTION("PS3ROMStorageDriver");+MODULE_AUTHOR("SonyCorporation");
--
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
Why do you have separate constants for PS3_DEV_TYPE_* and
PS3_MATCH_ID_*? If you don't do any conversion, this driver
will immediately work for additional types as well, if more
get added later.
+
+// pr_debug("%s:%u: Checking for new storage devices...\n",
+// __func__, __LINE__);
Should be removed, or not in comments, either way is fine, as pr_debug
normally does not get compiled in anyway.
+
+ msleep_interruptible(ms);
+ if (ms < 60000)
+ ms <<= 1;
Is this timeout only for the disk spinup, or also for detecting media
added at run time, like inserting a DVD? One minute timeout for
detecting a DVD would sound very long to me.
Arnd <><
So the hypervison uses guest-real addresses here? I would have expected
it to use the kernel page tables, which lets you use vmap() to do
scatter-gather.
I don't really understand what the kthread is needed for. You probably
thought about multiple options and ended up with this, but having
a comment in front of it might be helpful.
Arnd <><
On Friday 25 May 2007, Geert.Uytterhoeven@sonycom.com wrote:
Add a CD/DVD/BD Storage Driver for the PS3:
- Implemented as a SCSI device driver
I assume you tried implementing it as a block device driver,
like you PS3 disk driver does, and failed for some reason.
What is the problem? Is there infrastructure missing in the
CD-ROM layer?
Arnd <><
Why do you have separate constants for PS3_DEV_TYPE_* and
PS3_MATCH_ID_*? If you don't do any conversion, this driver
will immediately work for additional types as well, if more
get added later.
I noticed we have some redundancy in the constants and such
now that we have unified the device support. I planned to go
through and try to clean up what I can.
-Geoff
On Friday 25 May 2007, Geert.Uytterhoeven@sonycom.com wrote:
quoted
Add a CD/DVD/BD Storage Driver for the PS3:
- Implemented as a SCSI device driver
I assume you tried implementing it as a block device driver,
like you PS3 disk driver does, and failed for some reason.
What is the problem? Is there infrastructure missing in the
CD-ROM layer?
As the CD/DVD/BD part just accepts SCSI/ATAPI commands (except for plain
read/write), I was suggested to keep it as a SCSI driver.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
So the hypervison uses guest-real addresses here? I would have expected
it to use the kernel page tables, which lets you use vmap() to do
scatter-gather.
Yes, it uses logical partion addresses, so we cannot create a virtually
contiguous mapping.
I don't really understand what the kthread is needed for. You probably
thought about multiple options and ended up with this, but having
a comment in front of it might be helpful.
I used a kthread because the request function of a block device driver must be
non-blocking, and ps3stor_read_write_sectors() calls wait_for_completion().
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
On Fri, May 25, Geert.Uytterhoeven@sonycom.com wrote:
quoted
Add a Disk Storage Driver for the PS3:
There is no device symlink in /sys/block/ps3da/
Interesting... Do you know how to create it?
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
Why do you have separate constants for PS3_DEV_TYPE_* and
PS3_MATCH_ID_*? If you don't do any conversion, this driver
will immediately work for additional types as well, if more
get added later.
The PS3_DEV_TYPE_* IDs are imposed by the repository, as created by the
hypervisor.
The PS3_MATCH_ID_* IDs are created by us, for all PS3-specific devices.
As Geoff already pointed out, we may be able to use PS3_DEV_TYPE_* IDs for
everything, but unfortunately not all PS3-specific devices are present in the
repository.
quoted
+
+// pr_debug("%s:%u: Checking for new storage devices...\n",
+// __func__, __LINE__);
Should be removed, or not in comments, either way is fine, as pr_debug
normally does not get compiled in anyway.
Oops, forgot to uncomment it (it was a bit noisy while I lived with DEBUG
defined ;-)
quoted
+ msleep_interruptible(ms);
+ if (ms < 60000)
+ ms <<= 1;
Is this timeout only for the disk spinup, or also for detecting media
added at run time, like inserting a DVD? One minute timeout for
detecting a DVD would sound very long to me.
It's not for inserting DVDs, only for new devices showing up in the repository.
Apparently new devices may keep on showing up a while after boot up, but I
think this matters only for the kboot kernel, that's why I went with the
exponential back-off with upper limit.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
I don't really understand what the kthread is needed for. You probably
thought about multiple options and ended up with this, but having
a comment in front of it might be helpful.
I used a kthread because the request function of a block device driver must be
non-blocking, and ps3stor_read_write_sectors() calls wait_for_completion().
Ok, but why does it call wait_for_completion() then?
I thought you could end_that_request_* from the interrupt handler instead.
Arnd <><
What is the problem? Is there infrastructure missing in the
CD-ROM layer?
As the CD/DVD/BD part just accepts SCSI/ATAPI commands (except for plain
read/write), I was suggested to keep it as a SCSI driver.
Ok, so I guess the tradeoff here is that by writing it as a SCSI
driver, you don't need to implement any of the cdrom_device_ops
yourself but instead need to fake a few of the SCSI commands
in ps3rom_request(). Fair enough.
Arnd <><
I don't really understand what the kthread is needed for. You probably
thought about multiple options and ended up with this, but having
a comment in front of it might be helpful.
I used a kthread because the request function of a block device driver must be
non-blocking, and ps3stor_read_write_sectors() calls wait_for_completion().
Ok, but why does it call wait_for_completion() then?
I thought you could end_that_request_* from the interrupt handler instead.
Actually I tried that first, but I ran into other problems, like my request
handler being called continuously and requests gotten stuck. But maybe it was
just a locking bug on my side.
I can retry, but a disadvantage will be that there will be less code shared
with ps3flash and ps3rom.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
As I said multiple times, imho, #ifdef CONFIG_xxx_MODULE in the kernel
is always a bug.
You should always be able to build the module out of tree afteward and
use it on a kernel that didn't have the CONFIG_xxx_MODULE set imho.
Ben.
Ok, but why does it call wait_for_completion() then?
I thought you could end_that_request_* from the interrupt handler instead.
Actually I tried that first, but I ran into other problems, like my request
handler being called continuously and requests gotten stuck. But maybe it was
just a locking bug on my side.
I can retry, but a disadvantage will be that there will be less code shared
with ps3flash and ps3rom.
Not sure how much difference it will make performance-wise, but the context
switch for each bio adds some extra cost at least. Changing it
means you no longer share the ps3stor_read_write_sectors, but can at
the same time simplify the disk driver, so that won't hurt in total.
I don't care much, but I think it's worth trying.
Arnd <><
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2007-05-25 22:49:34
So the hypervison uses guest-real addresses here? I would have expected
it to use the kernel page tables, which lets you use vmap() to do
scatter-gather.
Ugh ? Maybe s390 can do that but no other hypervisor that I know
about :-) It would be nice, sure, but heh.
I don't really understand what the kthread is needed for. You probably
thought about multiple options and ended up with this, but having
a comment in front of it might be helpful.
Yeah, me neither... the driver looks very very very unefficient to me. I
though the kthread was useful for hotplug detection becasue the
hypervisor don't signal us, but from the patch, it looks like it's also
used for actual request processing which is very yucky.
Ben.
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2007-05-25 22:53:46
On Fri, 2007-05-25 at 21:40 +0200, Geert Uytterhoeven wrote:
I used a kthread because the request function of a block device driver
must be
non-blocking, and ps3stor_read_write_sectors() calls
wait_for_completion().
Which as I said before looks terribly sad... Why the heck would it have
to do that ?
Ben.
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2007-05-25 22:54:51
On Fri, 2007-05-25 at 21:48 +0200, Geert Uytterhoeven wrote:
quoted
quoted
+ msleep_interruptible(ms);
+ if (ms < 60000)
+ ms <<= 1;
Is this timeout only for the disk spinup, or also for detecting
media
quoted
added at run time, like inserting a DVD? One minute timeout for
detecting a DVD would sound very long to me.
It's not for inserting DVDs, only for new devices showing up in the
repository.
Apparently new devices may keep on showing up a while after boot up,
but I
think this matters only for the kboot kernel, that's why I went with
the
exponential back-off with upper limit.
Why not just have a kthread poll at 2 second interval for new devices or
removed ones ?
(And not for request processing)
Ben.
As I said multiple times, imho, #ifdef CONFIG_xxx_MODULE in the kernel
is always a bug.
You should always be able to build the module out of tree afteward and
use it on a kernel that didn't have the CONFIG_xxx_MODULE set imho.
I know.
Do you know another way to allocate an aligned chunk of 256 KiB of physically
contiguous memory, possibly a long time after boot up?
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
On Sat, 26 May 2007, Benjamin Herrenschmidt wrote:
On Fri, 2007-05-25 at 13:24 +0200, Olaf Hering wrote:
quoted
On Fri, May 25, Geert.Uytterhoeven@sonycom.com wrote:
quoted
+++ b/drivers/scsi/ps3rom.c
quoted
+ kaddr = kmap_atomic(sgpnt->page, KM_USER0);
linux/highmem.h is not included to get the kmap_* prototypes.
Beside, I don't see the point of using kmap on ppc64...
So what should I use instead?
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
Any reason not to use msleep(1) instead of the schedule_timeout?
Both look equally ugly though... do you really have to poll ?
The special notification device (NOTIFICATION_DEVID = -1) is not in the
repository and AFAIK it doesn't have an interrupt attached to it.
Note that this is used during probing only.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2007-05-26 22:19:28
On Sat, 2007-05-26 at 10:52 +0200, Geert Uytterhoeven wrote:
On Sat, 26 May 2007, Benjamin Herrenschmidt wrote:
quoted
On Fri, 2007-05-25 at 13:24 +0200, Olaf Hering wrote:
quoted
On Fri, May 25, Geert.Uytterhoeven@sonycom.com wrote:
quoted
+++ b/drivers/scsi/ps3rom.c
quoted
+ kaddr = kmap_atomic(sgpnt->page, KM_USER0);
linux/highmem.h is not included to get the kmap_* prototypes.
Beside, I don't see the point of using kmap on ppc64...
So what should I use instead?
you don't need to map ... the linear mapping is there.... page_address()
should just work. But then, kmap will resolve to just that anyway so I
suppose it doesn't matter.
Ben.
From: Christoph Hellwig <hch@lst.de> Date: 2007-05-29 09:54:33
On Fri, May 25, 2007 at 10:36:14AM +0200, Geert.Uytterhoeven@sonycom.com wrote:
Add a FLASH ROM Storage Driver for the PS3:
- Implemented as a misc character device driver
- Uses a fixed 256 KiB buffer allocated from boot memory as the hypervisor
requires the writing of aligned 256 KiB blocks
Looks good, but please either make the driver aware of multiple devices
even if they can't happen currently, or alternatively error out in
->probe if of some reason it's called for a second device.
From: Christoph Hellwig <hch@lst.de> Date: 2007-05-29 09:55:48
On Sun, May 27, 2007 at 08:18:43AM +1000, Benjamin Herrenschmidt wrote:
quoted
quoted
quoted
linux/highmem.h is not included to get the kmap_* prototypes.
Beside, I don't see the point of using kmap on ppc64...
So what should I use instead?
you don't need to map ... the linear mapping is there.... page_address()
should just work. But then, kmap will resolve to just that anyway so I
suppose it doesn't matter.
Generally I'd prefer to use kmap everywhere, to keep code future-proof.
Similar to how I advocate using spinlocks even in drivers for UP-only
architectures.
On Fri, May 25, 2007 at 10:36:14AM +0200, Geert.Uytterhoeven@sonycom.com wrote:
quoted
Add a FLASH ROM Storage Driver for the PS3:
- Implemented as a misc character device driver
- Uses a fixed 256 KiB buffer allocated from boot memory as the hypervisor
requires the writing of aligned 256 KiB blocks
Looks good, but please either make the driver aware of multiple devices
even if they can't happen currently, or alternatively error out in
->probe if of some reason it's called for a second device.
ps3flash_probe() does return -EBUSY when called for a second device.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
none of these seem to be used at all in the driver.
+
+#ifdef DEBUG
+static const char *scsi_command(unsigned char cmd)
+{
+ switch (cmd) {
+ case TEST_UNIT_READY: return "TEST_UNIT_READY/GPCMD_TEST_UNIT_READY";
+ case REZERO_UNIT: return "REZERO_UNIT";
+ case REQUEST_SENSE: return "REQUEST_SENSE/GPCMD_REQUEST_SENSE";
...
this kind of things shouldn't be in a low level driver. Either keep it
in your out of tree debug patches or if you feel adventurous send a
patch to linux-scsi that implements it in drivers/scsi/constant.c which
has debug code for other protocol-level scsi constants.
This seems rather pointless. The scsi_device has a pointer to the
host, so every access to scsi_dev->hostdata can simply be replaced
by an access through the host.
No need to keep your own scsi_done pointer. What you should do instead
in queuecommand is to set the scsi_done pointer in the scsi_cmnd here
and just use it later.
From: Christoph Hellwig <hch@lst.de> Date: 2007-05-29 10:51:59
On Fri, May 25, 2007 at 11:04:29PM +0200, Arnd Bergmann wrote:
On Friday 25 May 2007, Geert Uytterhoeven wrote:
quoted
quoted
What is the problem? Is there infrastructure missing in the
CD-ROM layer?
As the CD/DVD/BD part just accepts SCSI/ATAPI commands (except for plain
read/write), I was suggested to keep it as a SCSI driver.
Ok, so I guess the tradeoff here is that by writing it as a SCSI
driver, you don't need to implement any of the cdrom_device_ops
yourself but instead need to fake a few of the SCSI commands
in ps3rom_request(). Fair enough.
ps3rom is just a normal scsi low level driver. Currently the only
attached devices or MMC devices so the sr driver attaches to it,
but if you want to hack the ps3 hardware you should be able to
attach a tape or disk aswell, and linux would work (lv1 and gameos
might of course not like this)
From: Christoph Hellwig <hch@lst.de> Date: 2007-05-29 10:52:23
On Tue, May 29, 2007 at 11:57:52AM +0200, Geert Uytterhoeven wrote:
On Tue, 29 May 2007, Christoph Hellwig wrote:
quoted
On Fri, May 25, 2007 at 10:36:14AM +0200, Geert.Uytterhoeven@sonycom.com wrote:
quoted
Add a FLASH ROM Storage Driver for the PS3:
- Implemented as a misc character device driver
- Uses a fixed 256 KiB buffer allocated from boot memory as the hypervisor
requires the writing of aligned 256 KiB blocks
Looks good, but please either make the driver aware of multiple devices
even if they can't happen currently, or alternatively error out in
->probe if of some reason it's called for a second device.
ps3flash_probe() does return -EBUSY when called for a second device.
This looks very inefficient. Just set sg_tablesize of your driver
to 1 to avoid getting mutiple segments.
The disadvantage of setting sg_tablesize = 1 is that the driver will get small
requests (PAGE_SIZE) most of the time, which is very bad for performance.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2007-05-29 11:32:44
On Tue, 2007-05-29 at 13:11 +0200, Geert Uytterhoeven wrote:
quoted
This looks very inefficient. Just set sg_tablesize of your driver
to 1 to avoid getting mutiple segments.
The disadvantage of setting sg_tablesize = 1 is that the driver will
get small
requests (PAGE_SIZE) most of the time, which is very bad for
performance.
And the joke is that not only the HW can do scatter & gather but you
also have an iommu ...
Ben.
+/*
+ * copy data from device into scatter/gather buffer
+ */
+static int fill_from_dev_buffer(struct scsi_cmnd *cmd, const void *buf,
+ int buflen)
+{
+ int k, req_len, act_len, len, active;
+ void *kaddr;
+ struct scatterlist *sgpnt;
+
+ if (!cmd->request_bufflen)
+ return 0;
+
+ if (!cmd->request_buffer)
+ return DID_ERROR << 16;
+
+ if (cmd->sc_data_direction != DMA_BIDIRECTIONAL &&
+ cmd->sc_data_direction != DMA_FROM_DEVICE)
+ return DID_ERROR << 16;
+
+ if (!cmd->use_sg) {
+ req_len = cmd->request_bufflen;
+ act_len = min(req_len, buflen);
+ memcpy(cmd->request_buffer, buf, act_len);
+ cmd->resid = req_len - act_len;
+ return 0;
+ }
This is never true anymore.
Just to be sure: all four if-cases or only the last one?
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- Sony Network and Software Technology Center Europe (NSCE)
Geert.Uytterhoeven@sonycom.com ------- The Corporate Village, Da Vincilaan 7-D1
Voice +32-2-7008453 Fax +32-2-7008622 ---------------- B-1935 Zaventem, Belgium
From: Christoph Hellwig <hch@lst.de> Date: 2007-05-30 10:02:32
On Tue, May 29, 2007 at 06:21:36PM +0200, Geert Uytterhoeven wrote:
On Tue, 29 May 2007, Christoph Hellwig wrote:
quoted
quoted
+/*
+ * copy data from device into scatter/gather buffer
+ */
+static int fill_from_dev_buffer(struct scsi_cmnd *cmd, const void *buf,
+ int buflen)
+{
+ int k, req_len, act_len, len, active;
+ void *kaddr;
+ struct scatterlist *sgpnt;
+
+ if (!cmd->request_bufflen)
+ return 0;
+
+ if (!cmd->request_buffer)
+ return DID_ERROR << 16;
+
+ if (cmd->sc_data_direction != DMA_BIDIRECTIONAL &&
+ cmd->sc_data_direction != DMA_FROM_DEVICE)
+ return DID_ERROR << 16;
+
+ if (!cmd->use_sg) {
+ req_len = cmd->request_bufflen;
+ act_len = min(req_len, buflen);
+ memcpy(cmd->request_buffer, buf, act_len);
+ cmd->resid = req_len - act_len;
+ return 0;
+ }
This is never true anymore.
Just to be sure: all four if-cases or only the last one?
That's just in reference to the last one. The checks above could
be condensed a little more aswell, but I'll comment on further in
the second round of review, in the hope that the command submission
path is a lot more streamline by then already.
From: Christoph Hellwig <hch@lst.de> Date: 2007-05-30 10:14:36
On Tue, May 29, 2007 at 01:11:41PM +0200, Geert Uytterhoeven wrote:
quoted
This looks very inefficient. Just set sg_tablesize of your driver
to 1 to avoid getting mutiple segments.
The disadvantage of setting sg_tablesize = 1 is that the driver will get small
requests (PAGE_SIZE) most of the time, which is very bad for performance.
If you set .clustering = 1 in your host template you will frequently
get larger requests.
For any sane hypervisor or hardware the copy should be worth
than that. Then again a sane hardware or hypervisor would support
SG requests..