Commit c275257c authored by Ben Collins's avatar Ben Collins

[PATCH] IEEE-1394/Firewire updates

- Add driver registration for dv1394/video1394/raw1394.
- Fix 3 sleep-while-atomic bugs in ohci1394 and ieee1394.
- Cleanup some bus-reset handling in ohci1394.
- Add empty config-rom handling.
- Check and handle SBP-2 logins active/available for non-exclusive
  logins.
- Fix bug in SBP-2 DMA cleanup.
parent 48b2bd92
......@@ -116,6 +116,7 @@
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "ieee1394_hotplug.h"
#include "hosts.h"
#include "ieee1394_core.h"
#include "highlevel.h"
......@@ -140,7 +141,7 @@
(will cause undeflows if your machine is too slow!)
*/
#define DV1394_DEBUG_LEVEL 1
#define DV1394_DEBUG_LEVEL 0
/* for debugging use ONLY: allow more than one open() of the device */
/* #define DV1394_ALLOW_MORE_THAN_ONE_OPEN 1 */
......@@ -2469,6 +2470,32 @@ static int dv1394_devfs_add_entry(struct video_card *video)
}
#endif /* CONFIG_DEVFS_FS */
/*** HOTPLUG STUFF **********************************************************/
/*
* Export information about protocols/devices supported by this driver.
*/
static struct ieee1394_device_id dv1394_id_table[] = {
{
.match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
.specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
.version = AVC_SW_VERSION_ENTRY & 0xffffff
},
{ }
};
MODULE_DEVICE_TABLE(ieee1394, dv1394_id_table);
static struct hpsb_protocol_driver dv1394_driver = {
.name = "DV/1394 Driver",
.id_table = dv1394_id_table,
.driver = {
.name = "dv1394",
.bus = &ieee1394_bus_type,
},
};
/*** IEEE1394 HPSB CALLBACKS ***********************************************/
static int dv1394_init(struct ti_ohci *ohci, enum pal_or_ntsc format, enum modes mode)
......@@ -2897,6 +2924,8 @@ static void __exit dv1394_exit_module(void)
printk(KERN_ERR "dv1394: Error unregistering ioctl32 translations\n");
#endif
hpsb_unregister_protocol(&dv1394_driver);
hpsb_unregister_highlevel (hl_handle);
ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394);
#ifdef CONFIG_DEVFS_FS
......@@ -2947,6 +2976,8 @@ static int __init dv1394_init_module(void)
return -ENOMEM;
}
hpsb_register_protocol(&dv1394_driver);
#ifdef CONFIG_COMPAT
/* First compatible ones */
ret = register_ioctl32_conversion(DV1394_IOC_SHUTDOWN, NULL);
......
......@@ -164,7 +164,7 @@ void hpsb_add_host(struct hpsb_host *host)
spin_unlock_irqrestore(&hosts_lock, flags);
highlevel_add_host(host);
host->driver->devctl(host, RESET_BUS, 0);
host->driver->devctl(host, RESET_BUS, LONG_RESET);
}
void hpsb_remove_host(struct hpsb_host *host)
......
......@@ -11,6 +11,14 @@
#define IEEE1394_MATCH_SPECIFIER_ID 0x0004
#define IEEE1394_MATCH_VERSION 0x0008
/*
* Unit spec id and sw version entry for some protocols
*/
#define AVC_UNIT_SPEC_ID_ENTRY 0x0000A02D
#define AVC_SW_VERSION_ENTRY 0x00010001
#define CAMERA_UNIT_SPEC_ID_ENTRY 0x0000A02D
#define CAMERA_SW_VERSION_ENTRY 0x00000100
struct ieee1394_device_id {
u32 match_flags;
u32 vendor_id;
......
......@@ -620,6 +620,10 @@ static struct node_entry *nodemgr_scan_root_directory
if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
return NULL;
if (CONFIG_ROM_BUS_INFO_LENGTH(quad) == 1) /* minimal config rom */
return NULL;
address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4;
if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
......@@ -1332,6 +1336,13 @@ static int read_businfo_block(struct hpsb_host *host, nodeid_t nodeid, unsigned
header_size = buffer[0] >> 24;
addr += 4;
if (header_size == 1) {
HPSB_INFO("Node " NODE_BUS_FMT " has a minimal ROM. "
"Vendor is %08x",
NODE_BUS_ARGS(nodeid), buffer[0] & 0x00ffffff);
return -1;
}
if (header_size < 4) {
HPSB_INFO("Node " NODE_BUS_FMT " has non-standard ROM "
"format (%d quads), cannot parse",
......@@ -1767,6 +1778,7 @@ static void nodemgr_remove_host(struct hpsb_host *host)
break;
}
}
spin_unlock_irqrestore (&host_info_lock, flags);
if (hi) {
if (hi->pid >= 0) {
......@@ -1780,8 +1792,6 @@ static void nodemgr_remove_host(struct hpsb_host *host)
HPSB_ERR("NodeMgr: host %s does not exist, cannot remove",
host->driver->name);
spin_unlock_irqrestore (&host_info_lock, flags);
return;
}
......
......@@ -995,26 +995,13 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
return -EFAULT;
}
mask = (u64)0x1<<arg;
spin_lock_irqsave(&ohci->IR_channel_lock, flags);
if (ohci->ISO_channel_usage & mask) {
PRINT(KERN_ERR, ohci->id,
"%s: IS0 listen channel %d is already used",
__FUNCTION__, arg);
spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
return -EFAULT;
}
/* activate the legacy IR context */
if(ohci->ir_legacy_context.ohci == NULL) {
if(alloc_dma_rcv_ctx(ohci, &ohci->ir_legacy_context,
if (ohci->ir_legacy_context.ohci == NULL) {
if (alloc_dma_rcv_ctx(ohci, &ohci->ir_legacy_context,
DMA_CTX_ISO, 0, IR_NUM_DESC,
IR_BUF_SIZE, IR_SPLIT_BUF_SIZE,
OHCI1394_IsoRcvContextBase) < 0) {
PRINT(KERN_ERR, ohci->id,
"%s: failed to allocate an IR context",
PRINT(KERN_ERR, ohci->id, "%s: failed to allocate an IR context",
__FUNCTION__);
return -ENOMEM;
}
......@@ -1024,6 +1011,18 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
DBGMSG(ohci->id, "ISO receive legacy context activated");
}
mask = (u64)0x1<<arg;
spin_lock_irqsave(&ohci->IR_channel_lock, flags);
if (ohci->ISO_channel_usage & mask) {
PRINT(KERN_ERR, ohci->id,
"%s: IS0 listen channel %d is already used",
__FUNCTION__, arg);
spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
return -EFAULT;
}
ohci->ISO_channel_usage |= mask;
ohci->ir_legacy_channels |= mask;
......@@ -2318,7 +2317,7 @@ static void ohci_irq_handler(int irq, void *dev_id,
* or out manually into a port! The forced reset seems
* to solve this problem. This mainly effects nForce2. */
if (loop_count > 10000) {
hpsb_reset_bus(host, 1);
ohci_devctl(host, RESET_BUS, LONG_RESET);
DBGMSG(ohci->id, "Detected bus-reset loop. Forced a bus reset!");
loop_count = 0;
}
......
......@@ -49,6 +49,7 @@
#include "highlevel.h"
#include "iso.h"
#include "ieee1394_transactions.h"
#include "ieee1394_hotplug.h"
#include "raw1394.h"
#include "raw1394-private.h"
......@@ -2474,6 +2475,40 @@ static int raw1394_release(struct inode *inode, struct file *file)
return 0;
}
/*** HOTPLUG STUFF **********************************************************/
/*
* Export information about protocols/devices supported by this driver.
*/
static struct ieee1394_device_id raw1394_id_table[] = {
{
.match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
.specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
.version = AVC_SW_VERSION_ENTRY & 0xffffff
},
{
.match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
.specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
.version = CAMERA_SW_VERSION_ENTRY & 0xffffff
},
{ }
};
MODULE_DEVICE_TABLE(ieee1394, raw1394_id_table);
static struct hpsb_protocol_driver raw1394_driver = {
.name = "raw1394 Driver",
.id_table = raw1394_id_table,
.driver = {
.name = "raw1394",
.bus = &ieee1394_bus_type,
},
};
/******************************************************************************/
static struct hpsb_highlevel_ops hl_ops = {
.add_host = add_host,
.remove_host = remove_host,
......@@ -2515,12 +2550,17 @@ static int __init init_raw1394(void)
hpsb_unregister_highlevel(hl_handle);
return -EBUSY;
}
printk(KERN_INFO "raw1394: /dev/%s device initialized\n", RAW1394_DEVICE_NAME);
hpsb_register_protocol(&raw1394_driver);
return 0;
}
static void __exit cleanup_raw1394(void)
{
hpsb_unregister_protocol(&raw1394_driver);
ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_RAW1394);
devfs_unregister(devfs_handle);
hpsb_unregister_highlevel(hl_handle);
......
......@@ -641,7 +641,7 @@ static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_i
spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
for (i = 0; i < orbs; i++) {
command = (struct sbp2_command_info *)
kmalloc(sizeof(struct sbp2_command_info), GFP_KERNEL);
kmalloc(sizeof(struct sbp2_command_info), GFP_ATOMIC);
if (!command) {
spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
return(-ENOMEM);
......@@ -1049,6 +1049,22 @@ static int sbp2_start_device(struct sbp2scsi_host_info *hi, struct unit_director
goto alloc_fail;
SBP2_DMA_ALLOC("consistent DMA region for login FIFO");
/* Query logins ORB DMA */
scsi_id->query_logins_orb =
pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_query_logins_orb),
&scsi_id->query_logins_orb_dma);
if (!scsi_id->query_logins_orb)
goto alloc_fail;
SBP2_DMA_ALLOC("consistent DMA region for query logins ORB");
/* Query logins response DMA */
scsi_id->query_logins_response =
pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_query_logins_response),
&scsi_id->query_logins_response_dma);
if (!scsi_id->query_logins_response)
goto alloc_fail;
SBP2_DMA_ALLOC("consistent DMA region for query logins response");
/* Reconnect ORB DMA */
scsi_id->reconnect_orb =
pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_reconnect_orb),
......@@ -1071,6 +1087,22 @@ static int sbp2_start_device(struct sbp2scsi_host_info *hi, struct unit_director
&scsi_id->login_orb_dma);
if (!scsi_id->login_orb) {
alloc_fail:
if (scsi_id->query_logins_response) {
pci_free_consistent(hi->host->pdev,
sizeof(struct sbp2_query_logins_response),
scsi_id->query_logins_response,
scsi_id->query_logins_response_dma);
SBP2_DMA_FREE("query logins response DMA");
}
if (scsi_id->query_logins_orb) {
pci_free_consistent(hi->host->pdev,
sizeof(struct sbp2_query_logins_orb),
scsi_id->query_logins_orb,
scsi_id->query_logins_orb_dma);
SBP2_DMA_FREE("query logins ORB DMA");
}
if (scsi_id->logout_orb) {
pci_free_consistent(hi->host->pdev,
sizeof(struct sbp2_logout_orb),
......@@ -1241,10 +1273,26 @@ static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id)
pci_free_consistent(hi->host->pdev,
sizeof(struct sbp2_logout_orb),
scsi_id->logout_orb,
scsi_id->reconnect_orb_dma);
scsi_id->logout_orb_dma);
SBP2_DMA_FREE("single logout orb");
}
if (scsi_id->query_logins_orb) {
pci_free_consistent(hi->host->pdev,
sizeof(struct sbp2_query_logins_orb),
scsi_id->query_logins_orb,
scsi_id->query_logins_orb_dma);
SBP2_DMA_FREE("single query logins orb");
}
if (scsi_id->query_logins_response) {
pci_free_consistent(hi->host->pdev,
sizeof(struct sbp2_query_logins_response),
scsi_id->query_logins_response,
scsi_id->query_logins_response_dma);
SBP2_DMA_FREE("single query logins data");
}
SBP2_DEBUG("SBP-2 device removed, SCSI ID = %d", scsi_id->id);
kfree(scsi_id);
......@@ -1299,6 +1347,103 @@ static __inline__ int sbp2_command_conversion_device_type(u8 device_type)
(device_type == TYPE_ROM)) ? 1:0);
}
/*
* This function queries the device for the maximum concurrent logins it
* supports.
*/
static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id)
{
struct sbp2scsi_host_info *hi = scsi_id->hi;
quadlet_t data[2];
int max_logins;
int active_logins;
SBP2_DEBUG("sbp2_query_logins");
scsi_id->query_logins_orb->reserved1 = 0x0;
scsi_id->query_logins_orb->reserved2 = 0x0;
scsi_id->query_logins_orb->query_response_lo = scsi_id->query_logins_response_dma;
scsi_id->query_logins_orb->query_response_hi = ORB_SET_NODE_ID(hi->host->node_id);
SBP2_DEBUG("sbp2_query_logins: query_response_hi/lo initialized");
scsi_id->query_logins_orb->lun_misc = ORB_SET_FUNCTION(QUERY_LOGINS_REQUEST);
scsi_id->query_logins_orb->lun_misc |= ORB_SET_NOTIFY(1);
if (scsi_id->sbp2_device_type_and_lun != SBP2_DEVICE_TYPE_LUN_UNINITIALIZED) {
scsi_id->query_logins_orb->lun_misc |= ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun);
}
SBP2_DEBUG("sbp2_query_logins: lun_misc initialized");
scsi_id->query_logins_orb->reserved_resp_length =
ORB_SET_QUERY_LOGINS_RESP_LENGTH(sizeof(struct sbp2_query_logins_response));
SBP2_DEBUG("sbp2_query_logins: reserved_resp_length initialized");
scsi_id->query_logins_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO +
SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->id);
scsi_id->query_logins_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) |
SBP2_STATUS_FIFO_ADDRESS_HI);
SBP2_DEBUG("sbp2_query_logins: status FIFO initialized");
sbp2util_cpu_to_be32_buffer(scsi_id->query_logins_orb, sizeof(struct sbp2_query_logins_orb));
SBP2_DEBUG("sbp2_query_logins: orb byte-swapped");
sbp2util_packet_dump(scsi_id->query_logins_orb, sizeof(stuct sbp2_query_logins_orb),
"sbp2 query logins orb", scsi_id->query_logins_orb_dma);
memset(scsi_id->query_logins_response, 0, sizeof(struct sbp2_query_logins_response));
memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block));
SBP2_DEBUG("sbp2_query_logins: query_logins_response/status FIFO memset");
data[0] = ORB_SET_NODE_ID(hi->host->node_id);
data[1] = scsi_id->query_logins_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
atomic_set(&scsi_id->sbp2_login_complete, 0);
SBP2_DEBUG("sbp2_query_logins: prepared to write");
hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8);
SBP2_DEBUG("sbp2_query_logins: written");
if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, 2*HZ)) {
SBP2_ERR("Error querying logins to SBP-2 device - timed out");
return(-EIO);
}
if (scsi_id->status_block.ORB_offset_lo != scsi_id->query_logins_orb_dma) {
SBP2_ERR("Error querying logins to SBP-2 device - timed out");
return(-EIO);
}
if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) ||
STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) ||
STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) {
SBP2_ERR("Error querying logins to SBP-2 device - timed out");
return(-EIO);
}
sbp2util_cpu_to_be32_buffer(scsi_id->query_logins_response, sizeof(struct sbp2_query_logins_response));
SBP2_DEBUG("length_max_logins = %x",
(unsigned int)scsi_id->query_logins_response->length_max_logins);
SBP2_INFO("Query logins to SBP-2 device successful");
max_logins = RESPONSE_GET_MAX_LOGINS(scsi_id->query_logins_response->length_max_logins);
SBP2_INFO("Maximum concurrent logins supported: %d", max_logins);
active_logins = RESPONSE_GET_ACTIVE_LOGINS(scsi_id->query_logins_response->length_max_logins);
SBP2_INFO("Number of active logins: %d", active_logins);
if (active_logins >= max_logins) {
return(-EIO);
}
return 0;
}
/*
* This function is called in order to login to a particular SBP-2 device,
* after a bus reset.
......@@ -1315,6 +1460,13 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id)
return(-EIO);
}
if (!exclusive_login) {
if (sbp2_query_logins(scsi_id)) {
SBP2_ERR("Device does not support any more concurrent logins");
return(-EIO);
}
}
/* Set-up login ORB, assume no password */
scsi_id->login_orb->password_hi = 0;
scsi_id->login_orb->password_lo = 0;
......@@ -2563,6 +2715,7 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
* It's probably a login/logout/reconnect status.
*/
if ((scsi_id->login_orb_dma == scsi_id->status_block.ORB_offset_lo) ||
(scsi_id->query_logins_orb_dma == scsi_id->status_block.ORB_offset_lo) ||
(scsi_id->reconnect_orb_dma == scsi_id->status_block.ORB_offset_lo) ||
(scsi_id->logout_orb_dma == scsi_id->status_block.ORB_offset_lo)) {
atomic_set(&scsi_id->sbp2_login_complete, 1);
......
......@@ -93,6 +93,29 @@ struct sbp2_login_response {
#define ORB_SET_LOGIN_ID(value) (value & 0xffff)
#define ORB_SET_QUERY_LOGINS_RESP_LENGTH(value) (value & 0xffff)
struct sbp2_query_logins_orb {
u32 reserved1;
u32 reserved2;
u32 query_response_hi;
u32 query_response_lo;
u32 lun_misc;
u32 reserved_resp_length;
u32 status_FIFO_hi;
u32 status_FIFO_lo;
};
#define RESPONSE_GET_MAX_LOGINS(value) (value & 0xffff)
#define RESPONSE_GET_ACTIVE_LOGINS(value) ((RESPONSE_GET_LENGTH(value) - 4) / 12)
struct sbp2_query_logins_response {
u32 length_max_logins;
u32 misc_IDs;
u32 initiator_misc_hi;
u32 initiator_misc_lo;
};
struct sbp2_reconnect_orb {
u32 reserved1;
u32 reserved2;
......@@ -340,6 +363,10 @@ struct scsi_id_instance_data {
dma_addr_t login_orb_dma;
struct sbp2_login_response *login_response;
dma_addr_t login_response_dma;
struct sbp2_query_logins_orb *query_logins_orb;
dma_addr_t query_logins_orb_dma;
struct sbp2_query_logins_response *query_logins_response;
dma_addr_t query_logins_response_dma;
struct sbp2_reconnect_orb *reconnect_orb;
dma_addr_t reconnect_orb_dma;
struct sbp2_logout_orb *logout_orb;
......@@ -365,7 +392,7 @@ struct scsi_id_instance_data {
u32 sbp2_firmware_revision;
/*
* Variable used for logins, reconnects, logouts
* Variable used for logins, reconnects, logouts, query logins
*/
atomic_t sbp2_login_complete;
......@@ -457,6 +484,7 @@ static int sbp2_handle_physdma_read(struct hpsb_host *host, int nodeid, quadlet_
/*
* SBP-2 protocol related prototypes
*/
static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id);
static int sbp2_login_device(struct scsi_id_instance_data *scsi_id);
static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id);
static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id);
......
......@@ -48,6 +48,7 @@
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "ieee1394_hotplug.h"
#include "hosts.h"
#include "ieee1394_core.h"
#include "highlevel.h"
......@@ -1247,6 +1248,31 @@ static struct file_operations video1394_fops=
.release = video1394_release
};
/*** HOTPLUG STUFF **********************************************************/
/*
* Export information about protocols/devices supported by this driver.
*/
static struct ieee1394_device_id video1394_id_table[] = {
{
.match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
.specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
.version = CAMERA_SW_VERSION_ENTRY & 0xffffff
},
{ }
};
MODULE_DEVICE_TABLE(ieee1394, video1394_id_table);
static struct hpsb_protocol_driver video1394_driver = {
.name = "1394 Digital Camera Driver",
.id_table = video1394_id_table,
.driver = {
.name = VIDEO1394_DRIVER_NAME,
.bus = &ieee1394_bus_type,
},
};
static int video1394_init(struct ti_ohci *ohci)
{
struct video_card *video;
......@@ -1467,6 +1493,8 @@ static void __exit video1394_exit_module (void)
PRINT_G(KERN_INFO, "Error unregistering ioctl32 translations");
#endif
hpsb_unregister_protocol(&video1394_driver);
hpsb_unregister_highlevel (hl_handle);
devfs_unregister(devfs_handle);
......@@ -1496,6 +1524,8 @@ static int __init video1394_init_module (void)
return -ENOMEM;
}
hpsb_register_protocol(&video1394_driver);
#ifdef CONFIG_COMPAT
/* First the compatible ones */
ret = register_ioctl32_conversion(VIDEO1394_IOC_LISTEN_CHANNEL, NULL);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment