- 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.
#include "ieee1394.h"
#include "ieee1394_types.h"
+#include "ieee1394_hotplug.h"
#include "hosts.h"
#include "ieee1394_core.h"
#include "highlevel.h"
(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 */
}
#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)
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
return -ENOMEM;
}
+ hpsb_register_protocol(&dv1394_driver);
+
#ifdef CONFIG_COMPAT
/* First compatible ones */
ret = register_ioctl32_conversion(DV1394_IOC_SHUTDOWN, NULL);
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)
#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;
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))
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",
break;
}
}
+ spin_unlock_irqrestore (&host_info_lock, flags);
if (hi) {
if (hi->pid >= 0) {
HPSB_ERR("NodeMgr: host %s does not exist, cannot remove",
host->driver->name);
- spin_unlock_irqrestore (&host_info_lock, flags);
-
return;
}
return -EFAULT;
}
+ /* activate the legacy IR 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",
+ __FUNCTION__);
+ return -ENOMEM;
+ }
+ ohci->ir_legacy_channels = 0;
+ initialize_dma_rcv_ctx(&ohci->ir_legacy_context, 1);
+
+ 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) {
return -EFAULT;
}
- /* activate the legacy IR 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",
- __FUNCTION__);
- return -ENOMEM;
- }
- ohci->ir_legacy_channels = 0;
- initialize_dma_rcv_ctx(&ohci->ir_legacy_context, 1);
-
- DBGMSG(ohci->id, "ISO receive legacy context activated");
- }
-
ohci->ISO_channel_usage |= mask;
ohci->ir_legacy_channels |= mask;
* 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;
}
#include "highlevel.h"
#include "iso.h"
#include "ieee1394_transactions.h"
+#include "ieee1394_hotplug.h"
#include "raw1394.h"
#include "raw1394-private.h"
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,
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);
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);
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),
&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),
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);
(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.
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;
* 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);
#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;
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;
u32 sbp2_firmware_revision;
/*
- * Variable used for logins, reconnects, logouts
+ * Variable used for logins, reconnects, logouts, query logins
*/
atomic_t sbp2_login_complete;
/*
* 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);
#include "ieee1394.h"
#include "ieee1394_types.h"
+#include "ieee1394_hotplug.h"
#include "hosts.h"
#include "ieee1394_core.h"
#include "highlevel.h"
.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;
PRINT_G(KERN_INFO, "Error unregistering ioctl32 translations");
#endif
+ hpsb_unregister_protocol(&video1394_driver);
+
hpsb_unregister_highlevel (hl_handle);
devfs_unregister(devfs_handle);
return -ENOMEM;
}
+ hpsb_register_protocol(&video1394_driver);
+
#ifdef CONFIG_COMPAT
/* First the compatible ones */
ret = register_ioctl32_conversion(VIDEO1394_IOC_LISTEN_CHANNEL, NULL);