libata: check for AN support
Subsystems (3)
PATCH 1/3 libata: check for AN support
In reply to a message not in the archiveCheck to see if an ATAPI device supports Asynchronous Notification. If so, enable it.
drivers/ata/libata-core.c +54 -0
@@ -67,6 +67,7 @@static unsigned int ata_dev_init_params (struct ata_device *dev,u16 heads,u16 sectors);static unsigned int ata_dev_set_xfermode (struct ata_device *dev); +static unsigned int ata_dev_set_an (struct ata_device *dev);static void ata_dev_xfermask (struct ata_device *dev);static unsigned int ata_unique_id =1 ;
@@ -1739,6 +1740,23 @@ } dev->cdb_len = (unsigned int ) rc; +/* +* check to see if this ATAPI device supports +* Asynchronous Notification +*/ +if (ata_id_has_an (id)) + { +/* issue SET feature command to turn this on */ + rc =ata_dev_set_an (dev); +if (rc) { +ata_dev_printk (dev,KERN_ERR , +"unable to set AN\n" ); + rc = -EINVAL ; + goto err_out_nosup; + } + dev->flags |=ATA_DFLAG_AN ; + } +if (ata_id_cdb_intr (dev->id)) { dev->flags |=ATA_DFLAG_CDB_INTR ; cdb_intr_string =", CDB intr" ;
@@ -3479,6 +3497,42 @@ }/** +* ata_dev_set_an - Issue SET FEATURES - SATA FEATURES +* with sector count set to indicate +* Asynchronous Notification feature +* @dev: Device to which command will be sent +* +* Issue SET FEATURES - SATA FEATURES command to device @dev +* on port @ap. +* +* LOCKING: +* PCI/etc. bus probe sem. +* +* RETURNS: +* 0 on success, AC_ERR_* mask otherwise. +*/ +static unsigned int ata_dev_set_an (struct ata_device *dev) +{ +struct ata_taskfile tf; +unsigned int err_mask; + +/* set up set-features taskfile */ +DPRINTK ("set features - SATA features\n" ); + +ata_tf_init (dev, &tf); + tf.command =ATA_CMD_SET_FEATURES ; + tf.feature =SETFEATURES_SATA_ENABLE ; + tf.flags |=ATA_TFLAG_ISADDR |ATA_TFLAG_DEVICE ; + tf.protocol =ATA_PROT_NODATA ; + tf.nsect =SATA_AN ; + + err_mask =ata_exec_internal (dev, &tf,NULL ,DMA_NONE ,NULL ,0 ); + +DPRINTK ("EXIT, err_mask=%x\n" , err_mask); +return err_mask; +} + +/** * ata_dev_init_params - Issue INIT DEV PARAMS command * @dev: Device to which command will be sent * @heads: Number of heads (taskfile parameter)
include/linux/ata.h +8 -0
@@ -190,6 +190,12 @@SETFEATURES_WC_ON =0x02 ,/* Enable write cache */ SETFEATURES_WC_OFF =0x82 ,/* Disable write cache */ +SETFEATURES_SATA_ENABLE =0x10 ,/* Enable use of SATA feature */ +SETFEATURES_SATA_DISABLE =0x90 ,/* Disable use of SATA feature */ + +/* SETFEATURE Sector counts for SATA features */ +SATA_AN =0x05 ,/* Asynchronous Notification */ +/* ATAPI stuff */ ATAPI_PKT_DMA = (1 <<0 ), ATAPI_DMADIR = (1 << 2), /* ATAPI data dir:
@@ -296,6 +302,8 @@#define ata_id_queue_depth (id) (((id)[75] & 0x1f) + 1)#define ata_id_removeable (id) ((id)[0] & (1 << 7))#define ata_id_has_dword_io (id) ((id)[50] & (1 << 0)) +#define ata_id_has_an (id) \ + ((id[76] && (~id[76])) & ((id)[78] & (1 << 5)))#define ata_id_u32 (id,n) \ (((u32) (id)[(n) + 1] << 16) | ((u32) (id)[(n)]))#define ata_id_u64 (id,n) \
include/linux/libata.h +1 -0
@@ -141,6 +141,7 @@ATA_DFLAG_CDB_INTR = (1 <<2 ),/* device asserts INTRQ when ready for CDB */ ATA_DFLAG_NCQ = (1 <<3 ),/* device supports NCQ */ ATA_DFLAG_FLUSH_EXT = (1 <<4 ),/* do FLUSH_EXT instead of FLUSH */ +ATA_DFLAG_AN = (1 <<5 ),/* device supports Async notification */ ATA_DFLAG_CFG_MASK = (1 <<8 ) -1 , ATA_DFLAG_PIO = (1 << 8), /* device limited to PIO mode */
Check to see if an ATAPI device supports Asynchronous Notification. If so, enable it.
drivers/ata/libata-core.c +54 -0
72 lines not on this page · raw message
include/linux/ata.h +8 -0
@@ -190,6 +190,12 @@SETFEATURES_WC_ON =0x02 ,/* Enable write cache */ SETFEATURES_WC_OFF =0x82 ,/* Disable write cache */ +SETFEATURES_SATA_ENABLE =0x10 ,/* Enable use of SATA feature */ +SETFEATURES_SATA_DISABLE =0x90 ,/* Disable use of SATA feature */ + +/* SETFEATURE Sector counts for SATA features */ +SATA_AN =0x05 ,/* Asynchronous Notification */ +/* ATAPI stuff */ ATAPI_PKT_DMA = (1 <<0 ), ATAPI_DMADIR = (1 << 2), /* ATAPI data dir:
@@ -296,6 +302,8 @@#define ata_id_queue_depth (id) (((id)[75] & 0x1f) + 1)#define ata_id_removeable (id) ((id)[0] & (1 << 7))#define ata_id_has_dword_io (id) ((id)[50] & (1 << 0)) +#define ata_id_has_an (id) \ + ((id[76] && (~id[76])) & ((id)[78] & (1 << 5)))#define ata_id_u32 (id,n) \ (((u32) (id)[(n) + 1] << 16) | ((u32) (id)[(n)]))#define ata_id_u64 (id,n) \
include/linux/libata.h +1 -0
@@ -141,6 +141,7 @@ATA_DFLAG_CDB_INTR = (1 <<2 ),/* device asserts INTRQ when ready for CDB */ ATA_DFLAG_NCQ = (1 <<3 ),/* device supports NCQ */ ATA_DFLAG_FLUSH_EXT = (1 <<4 ),/* do FLUSH_EXT instead of FLUSH */ +ATA_DFLAG_AN = (1 <<5 ),/* device supports Async notification */ ATA_DFLAG_CFG_MASK = (1 <<8 ) -1 , ATA_DFLAG_PIO = (1 << 8), /* device limited to PIO mode */
PATCH 2/3 libata: expose AN support to user space via sysfs
In reply to a message not in the archiveAllow user space to determine if an ATAPI device supports async notification (AN) of media changes. This is done by adding a new sysfs file "async_notification" to genhd. If the file reads 1, then the device supports async notification. If the file reads 0, it does not.
A flag is set in the generic disk to indicate whether or not AN is supported. This flag is set by the SCSI subsystem when it registers with add_disk. The SCSI system gets information from libata on whether the device supports AN during dev_configure.
block/genhd.c +10 -0
@@ -369,6 +369,11 @@ {return sprintf (page,"%llu\n" , (unsigned long long )get_capacity (disk)); } +static ssize_t disk_an_read (struct gendisk * disk,char *page) +{ +return sprintf (page,"%d\n" , + (disk->flags &GENHD_FL_ASYNC_NOTIFICATION ?1 :0 )); +}static ssize_t disk_stats_read (struct gendisk * disk,char *page) {
@@ -416,6 +421,10 @@ .attr = {.name = "stat", .mode = S_IRUGO }, .show = disk_stats_read }; +static struct disk_attribute disk_attr_an = { + .attr = {.name ="async_notification" , .mode =S_IRUGO }, + .show = disk_an_read +};#ifdef CONFIG_FAIL_MAKE_REQUEST
@@ -452,6 +461,7 @@ &disk_attr_removable.attr, &disk_attr_size.attr, &disk_attr_stat.attr, + &disk_attr_an.attr, #ifdef CONFIG_FAIL_MAKE_REQUEST &disk_attr_fail.attr, #endif
include/linux/genhd.h +1 -0
@@ -90,6 +90,7 @@#define GENHD_FL_REMOVABLE 1#define GENHD_FL_DRIVERFS 2 +#define GENHD_FL_ASYNC_NOTIFICATION 4#define GENHD_FL_CD 8#define GENHD_FL_UP 16#define GENHD_FL_SUPPRESS_PARTITION_INFO 32
include/scsi/scsi_device.h +1 -1
@@ -123,7 +123,7 @@ unsigned select_no_atn:1; unsigned fix_capacity:1; /* READ_CAPACITY is too high by 1 */ unsigned retry_hwerror:1; /* Retry HARDWARE_ERROR */ - + unsigned async_notification:1; /* device supports async notification */unsigned int device_blocked;/* Device returned QUEUE_FULL. */ unsigned int max_device_blocked;/* what device_blocked counts down from */
drivers/ata/libata-scsi.c +3 -0
@@ -855,6 +855,9 @@blk_queue_max_hw_segments (q, q->max_hw_segments -1 ); } +if (dev->flags &ATA_DFLAG_AN ) + sdev->async_notification =1 ; +if (dev->flags &ATA_DFLAG_NCQ ) {int depth;
drivers/scsi/sr.c +2 -0
@@ -602,6 +602,8 @@dev_set_drvdata (dev, cd); disk->flags |=GENHD_FL_REMOVABLE ; +if (sdev->async_notification) + disk->flags |=GENHD_FL_ASYNC_NOTIFICATION ;add_disk (disk); sdev_printk(KERN_DEBUG, sdev,
Allow user space to determine if an ATAPI device supports async notification (AN) of media changes. This is done by adding a new sysfs file "async_notification" to genhd. If the file reads 1, then the device supports async notification. If the file reads 0, it does not.
A flag is set in the generic disk to indicate whether or not AN is supported. This flag is set by the SCSI subsystem when it registers with add_disk. The SCSI system gets information from libata on whether the device supports AN during dev_configure.
block/genhd.c +10 -0
@@ -369,6 +369,11 @@ {return sprintf (page,"%llu\n" , (unsigned long long )get_capacity (disk)); } +static ssize_t disk_an_read (struct gendisk * disk,char *page) +{ +return sprintf (page,"%d\n" , + (disk->flags &GENHD_FL_ASYNC_NOTIFICATION ?1 :0 )); +}static ssize_t disk_stats_read (struct gendisk * disk,char *page) {
@@ -416,6 +421,10 @@ .attr = {.name = "stat", .mode = S_IRUGO }, .show = disk_stats_read }; +static struct disk_attribute disk_attr_an = { + .attr = {.name ="async_notification" , .mode =S_IRUGO }, + .show = disk_an_read +};#ifdef CONFIG_FAIL_MAKE_REQUEST
@@ -452,6 +461,7 @@ &disk_attr_removable.attr, &disk_attr_size.attr, &disk_attr_stat.attr, + &disk_attr_an.attr, #ifdef CONFIG_FAIL_MAKE_REQUEST &disk_attr_fail.attr, #endif
include/linux/genhd.h +1 -0
@@ -90,6 +90,7 @@#define GENHD_FL_REMOVABLE 1#define GENHD_FL_DRIVERFS 2 +#define GENHD_FL_ASYNC_NOTIFICATION 4#define GENHD_FL_CD 8#define GENHD_FL_UP 16#define GENHD_FL_SUPPRESS_PARTITION_INFO 32
include/scsi/scsi_device.h +1 -1
@@ -123,7 +123,7 @@ unsigned select_no_atn:1; unsigned fix_capacity:1; /* READ_CAPACITY is too high by 1 */ unsigned retry_hwerror:1; /* Retry HARDWARE_ERROR */ - + unsigned async_notification:1; /* device supports async notification */unsigned int device_blocked;/* Device returned QUEUE_FULL. */ unsigned int max_device_blocked;/* what device_blocked counts down from */
drivers/ata/libata-scsi.c +3 -0
@@ -855,6 +855,9 @@blk_queue_max_hw_segments (q, q->max_hw_segments -1 ); } +if (dev->flags &ATA_DFLAG_AN ) + sdev->async_notification =1 ; +if (dev->flags &ATA_DFLAG_NCQ ) {int depth;
drivers/scsi/sr.c +2 -0
@@ -602,6 +602,8 @@dev_set_drvdata (dev, cd); disk->flags |=GENHD_FL_REMOVABLE ; +if (sdev->async_notification) + disk->flags |=GENHD_FL_ASYNC_NOTIFICATION ;add_disk (disk); sdev_printk(KERN_DEBUG, sdev,
PATCH 3/3 libata: handle AN interrupt
In reply to a message not in the archiveWhen we get an SDB FIS with the 'N' bit set, we should send an event to user space to indicate that there has been a media change. The ahci host controller will send the event via KOBJ_CHANGE uevent.
drivers/ata/ahci.c +20 -0
@@ -1126,6 +1126,26 @@return ; } +if (status &PORT_IRQ_SDB_FIS ) { +/* +* if this is an ATAPI device with AN turned on, +* then we should interrogate the device to +* determine the cause of the interrupt +* +* for AN - this we should check the SDB FIS +* and find the I and N bits set +*/ +const u32 *f = pp->rx_fis +RX_FIS_SDB ; + +/* check the 'N' bit in word 0 of the FIS */ +if (f[0 ] & (1 <<15 )) { +int port_addr = ((f[0 ] &0x00000f00 ) >>8 ); +struct ata_device *adev = &ap->device[port_addr]; +ata_port_printk (ap,KERN_INFO ,"N bit set on SDB FIS!\n" ); +if (adev->flags &ATA_DFLAG_AN ) +ata_async_notify (adev); + } + }if (ap->sactive) qc_active =readl (port_mmio +PORT_SCR_ACT );else
include/linux/libata.h +2 -0
@@ -504,6 +504,7 @@/* error history */ struct ata_ering ering;unsigned int horkage;/* List of broken features */ +struct work_struct async_notify; };/* Offset into struct ata_device. Fields above it are maintained
@@ -827,6 +828,7 @@extern int ata_scsi_change_queue_depth (struct scsi_device *sdev,int queue_depth);extern struct ata_device *ata_dev_pair (struct ata_device *adev); +extern void ata_async_notify (struct ata_device *atadev);/* * Timing helpers
drivers/ata/libata-core.c +22 -0
@@ -1589,6 +1589,26 @@ } } +static void async_notify_thread (struct work_struct *work) +{ +struct ata_device *atadev = + container_of(work, struct ata_device, async_notify); + +/* +* TBD - who should send this event? I couldn't find an +* easy way to map an ata_device to a genhd device, so +* decided maybe the ata host should send the event and +* allow user space to figure out what happened? +*/ +kobject_uevent (&atadev->ap->host->dev->kobj,KOBJ_CHANGE ); +} + +void ata_async_notify (struct ata_device *atadev) +{ +schedule_work (&atadev->async_notify); +} + +/** * ata_dev_configure - Configure the specified ATA/ATAPI device * @dev: Target device to configure
@@ -1755,6 +1775,7 @@ goto err_out_nosup; } dev->flags |=ATA_DFLAG_AN ; +INIT_WORK (&dev->async_notify, async_notify_thread); }if (ata_id_cdb_intr (dev->id)) {
@@ -6545,3 +6566,4 @@EXPORT_SYMBOL_GPL (ata_eh_qc_complete);EXPORT_SYMBOL_GPL (ata_eh_qc_retry);EXPORT_SYMBOL_GPL (ata_do_eh); +EXPORT_SYMBOL_GPL (ata_async_notify);
When we get an SDB FIS with the 'N' bit set, we should send an event to user space to indicate that there has been a media change. The ahci host controller will send the event via KOBJ_CHANGE uevent.
drivers/ata/ahci.c +20 -0
@@ -1126,6 +1126,26 @@return ; } +if (status &PORT_IRQ_SDB_FIS ) { +/* +* if this is an ATAPI device with AN turned on, +* then we should interrogate the device to +* determine the cause of the interrupt +* +* for AN - this we should check the SDB FIS +* and find the I and N bits set +*/ +const u32 *f = pp->rx_fis +RX_FIS_SDB ; + +/* check the 'N' bit in word 0 of the FIS */ +if (f[0 ] & (1 <<15 )) { +int port_addr = ((f[0 ] &0x00000f00 ) >>8 ); +struct ata_device *adev = &ap->device[port_addr]; +ata_port_printk (ap,KERN_INFO ,"N bit set on SDB FIS!\n" ); +if (adev->flags &ATA_DFLAG_AN ) +ata_async_notify (adev); + } + }if (ap->sactive) qc_active =readl (port_mmio +PORT_SCR_ACT );else
include/linux/libata.h +2 -0
@@ -504,6 +504,7 @@/* error history */ struct ata_ering ering;unsigned int horkage;/* List of broken features */ +struct work_struct async_notify; };/* Offset into struct ata_device. Fields above it are maintained
@@ -827,6 +828,7 @@extern int ata_scsi_change_queue_depth (struct scsi_device *sdev,int queue_depth);extern struct ata_device *ata_dev_pair (struct ata_device *adev); +extern void ata_async_notify (struct ata_device *atadev);/* * Timing helpers
drivers/ata/libata-core.c +22 -0
@@ -1589,6 +1589,26 @@ } } +static void async_notify_thread (struct work_struct *work) +{ +struct ata_device *atadev = + container_of(work, struct ata_device, async_notify); + +/* +* TBD - who should send this event? I couldn't find an +* easy way to map an ata_device to a genhd device, so +* decided maybe the ata host should send the event and +* allow user space to figure out what happened? +*/ +kobject_uevent (&atadev->ap->host->dev->kobj,KOBJ_CHANGE ); +} + +void ata_async_notify (struct ata_device *atadev) +{ +schedule_work (&atadev->async_notify); +} + +/** * ata_dev_configure - Configure the specified ATA/ATAPI device * @dev: Target device to configure
@@ -1755,6 +1775,7 @@ goto err_out_nosup; } dev->flags |=ATA_DFLAG_AN ; +INIT_WORK (&dev->async_notify, async_notify_thread); }if (ata_id_cdb_intr (dev->id)) {
@@ -6545,3 +6566,4 @@EXPORT_SYMBOL_GPL (ata_eh_qc_complete);EXPORT_SYMBOL_GPL (ata_eh_qc_retry);EXPORT_SYMBOL_GPL (ata_do_eh); +EXPORT_SYMBOL_GPL (ata_async_notify);