Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Marek <mmarek@suse.cz>2009-12-07 11:04:23 +0100
committerMichal Marek <mmarek@suse.cz>2009-12-07 11:04:23 +0100
commit89e4c87d574ac4fd2cc2395856de483b6a9ea9e9 (patch)
treef3d87c34c8f9b6d7a0dfa0427ec450e3110bf60a
parent3e95a81d8a98a66dc2fed5b7c3b0a6023ee8d836 (diff)
parenta4ccca56919a0d9edaaad27b6962366a60f8d097 (diff)
Merge branch 'master' into SLE11-SP1rpm-2.6.32-0.6.8
-rw-r--r--Documentation/ABI/stable/sysfs-driver-qla2xxx8
-rw-r--r--Documentation/kmsg/s390/cio53
-rw-r--r--Documentation/kmsg/s390/cpu4
-rw-r--r--Documentation/kmsg/s390/dasd-diag24
-rw-r--r--Documentation/kmsg/s390/dasd-eckd60
-rw-r--r--Documentation/kmsg/s390/setup12
-rw-r--r--Documentation/kmsg/s390/vmur7
-rw-r--r--Documentation/kmsg/s390/zfcp19
-rw-r--r--drivers/s390/net/qeth_core.h5
-rw-r--r--drivers/s390/net/qeth_core_main.c130
-rw-r--r--drivers/s390/net/qeth_core_mpc.h44
-rw-r--r--drivers/s390/net/qeth_core_sys.c8
-rw-r--r--drivers/s390/net/qeth_l2_main.c3
-rw-r--r--drivers/s390/net/qeth_l3.h2
-rw-r--r--drivers/s390/net/qeth_l3_main.c174
-rw-r--r--drivers/s390/net/qeth_l3_sys.c56
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c78
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h9
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h3
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c12
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c144
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c50
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c35
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h2
-rw-r--r--drivers/usb/misc/Kconfig39
-rw-r--r--drivers/usb/misc/Makefile4
-rw-r--r--drivers/usb/misc/phidget.c43
-rw-r--r--drivers/usb/misc/phidget.h12
-rw-r--r--drivers/usb/misc/phidgetkit.c740
-rw-r--r--drivers/usb/misc/phidgetmotorcontrol.c465
-rw-r--r--drivers/usb/misc/phidgetservo.c375
-rw-r--r--mm/page_io.c7
-rw-r--r--security/apparmor/.gitignore5
-rw-r--r--security/apparmor/apparmorfs.c3
-rw-r--r--security/apparmor/audit.c2
-rw-r--r--security/apparmor/capability.c3
-rw-r--r--security/apparmor/domain.c83
-rw-r--r--security/apparmor/file.c15
-rw-r--r--security/apparmor/include/apparmor.h (renamed from security/apparmor/include/security/apparmor.h)2
-rw-r--r--security/apparmor/include/apparmorfs.h (renamed from security/apparmor/include/security/apparmorfs.h)0
-rw-r--r--security/apparmor/include/file.h2
-rw-r--r--security/apparmor/include/policy.h6
-rw-r--r--security/apparmor/lsm.c29
-rw-r--r--security/apparmor/match.c22
-rw-r--r--security/apparmor/net.c4
-rw-r--r--security/apparmor/path.c34
-rw-r--r--security/apparmor/policy.c28
-rw-r--r--security/apparmor/policy_interface.c20
-rw-r--r--security/apparmor/procattr.c2
-rw-r--r--security/apparmor/sid.c4
53 files changed, 2532 insertions, 364 deletions
diff --git a/Documentation/ABI/stable/sysfs-driver-qla2xxx b/Documentation/ABI/stable/sysfs-driver-qla2xxx
new file mode 100644
index 000000000000..9a59d84497ed
--- /dev/null
+++ b/Documentation/ABI/stable/sysfs-driver-qla2xxx
@@ -0,0 +1,8 @@
+What: /sys/bus/pci/drivers/qla2xxx/.../devices/*
+Date: September 2009
+Contact: QLogic Linux Driver <linux-driver@qlogic.com>
+Description: qla2xxx-udev.sh currently looks for uevent CHANGE events to
+ signal a firmware-dump has been generated by the driver and is
+ ready for retrieval.
+Users: qla2xxx-udev.sh. Proposed changes should be mailed to
+ linux-driver@qlogic.com
diff --git a/Documentation/kmsg/s390/cio b/Documentation/kmsg/s390/cio
index a7fe5df540e6..6d209ba7631e 100644
--- a/Documentation/kmsg/s390/cio
+++ b/Documentation/kmsg/s390/cio
@@ -90,3 +90,56 @@
* See the errno man page to find out what caused the problem.
*/
/*? Text: "%s: Got subchannel machine check but no sch_event handler provided.\n" */
+
+/*?
+ * Text: "%s: Setting the device online failed because it is boxed\n"
+ * Severity: Warning
+ * Parameter:
+ * @1: Device bus-ID
+ * Description:
+ * Initialization of a device did not complete because it did not respond in
+ * time or it was reserved by another operating system.
+ * User action:
+ * Make sure that the device is working correctly, then try again to set it
+ * online. For devices that support the reserve/release mechanism (for example
+ * DASDs), you can try to override the reservation of the other system by
+ * writing 'force' to the 'online' sysfs attribute of the affected device.
+ */
+
+/*?
+ * Text: "%s: Setting the device online failed because it is not operational\n"
+ * Severity: Warning
+ * Parameter:
+ * @1: Device bus-ID
+ * Description:
+ * Initialization of a device did not complete because it is not present or
+ * not operational.
+ * User action:
+ * Make sure that the device is present and working correctly, then try again
+ * to set it online.
+ */
+
+/*?
+ * Text: "%s: The device stopped operating while being set offline\n"
+ * Severity: Warning
+ * Parameter:
+ * @1: Device bus-ID
+ * Description:
+ * While the device was set offline, it was not present or not operational.
+ * The device is now inactive, but setting it online again might fail.
+ * User action:
+ * None.
+ */
+
+/*?
+ * Text: "%s: The device entered boxed state while being set offline\n"
+ * Severity: Warning
+ * Parameter:
+ * @1: Device bus-ID
+ * Description:
+ * While the device was set offline, it did not respond in time or it was
+ * reserved by another operating system. The device is now inactive, but
+ * setting it online again might fail.
+ * User action:
+ * None.
+ */
diff --git a/Documentation/kmsg/s390/cpu b/Documentation/kmsg/s390/cpu
index 6cc3b2f63a58..fba2eca01857 100644
--- a/Documentation/kmsg/s390/cpu
+++ b/Documentation/kmsg/s390/cpu
@@ -38,8 +38,8 @@
* Text: "The CPU configuration topology of the machine is:"
* Severity: Informational
* Description:
- * The first six values of the topology information represent fields Mag1 to
- * Mag6 of system-information block (SYSIB) 15.1.2. These fields specify the
+ * The first six values of the topology information represent fields Mag6 to
+ * Mag1 of system-information block (SYSIB) 15.1.2. These fields specify the
* maximum numbers of topology-list entries (TLE) at successive topology nesting
* levels. The last value represents the MNest value of SYSIB 15.1.2 which
* specifies the maximum possible nesting that can be configured through
diff --git a/Documentation/kmsg/s390/dasd-diag b/Documentation/kmsg/s390/dasd-diag
index 374f3df8b420..d27686020909 100644
--- a/Documentation/kmsg/s390/dasd-diag
+++ b/Documentation/kmsg/s390/dasd-diag
@@ -28,14 +28,19 @@
*/
/*?
- * Text: "%s: New DASD with %ld byte/block, total size %ld KB\n"
+ * Text: "%s: New DASD with %ld byte/block, total size %ld KB%s\n"
* Severity: Informational
* Parameter:
* @1: bus ID of the DASD
* @2: bytes per block
* @3: size
+ * @4: access mode
* Description:
* A DASD with the indicated block size and total size has been set online.
+ * If the DASD is configured as read-only to the real or virtual hardware,
+ * the message includes an indication of this hardware access mode. The
+ * hardware access mode is independent from the 'readonly' attribute of
+ * the device in sysfs.
* User action:
* None.
*/
@@ -62,8 +67,8 @@
* @2: return code
* Description:
* Initializing the DASD with the DIAG discipline failed. Possible reasons for
- * this problem are that the device is read-only, has a device type other
- * than FBA or ECKD, or has a block size other than one of the supported sizes:
+ * this problem are that the device has a device type other than FBA or ECKD,
+ * or has a block size other than one of the supported sizes:
* 512 byte, 1024 byte, 2048 byte, or 4096 byte.
* User action:
* Ensure that the device can be written to and has a supported device type
@@ -98,3 +103,16 @@
* User action:
* Remove the unsupported discipline from the parameter string.
*/
+
+/*?
+ * Text: "%s: The access mode of a DIAG device changed to read-only"
+ * Severity: Warning
+ * Parameter:
+ * @1: bus ID of the DASD
+ * Description:
+ * A device changed its access mode from writeable to
+ * read-only while in use.
+ * User action:
+ * Set the device offline, ensure that the device is configured correctly in
+ * z/VM, then set the device online again.
+ */
diff --git a/Documentation/kmsg/s390/dasd-eckd b/Documentation/kmsg/s390/dasd-eckd
index bbc72889d175..39149af62b02 100644
--- a/Documentation/kmsg/s390/dasd-eckd
+++ b/Documentation/kmsg/s390/dasd-eckd
@@ -1839,3 +1839,63 @@
* User action:
* Look up the SRC in the storage server documentation.
*/
+
+/*?
+ * Text: "%s: Reading device feature codes failed with rc=%d\n"
+ * Severity: Warning
+ * Parameter:
+ * @1: bus ID of the DASD
+ * @2: return code
+ * Description:
+ * The device feature codes state which advanced features are supported by a
+ * device.
+ * Examples for advanced features are PAV or high performance FICON.
+ * Some early devices do not provide feature codes and no advanced features are
+ * available on these devices.
+ * User action:
+ * None, if the DASD does not provide feature codes. If the DASD provides
+ * feature codes, make sure that it is working correctly, then set it offline
+ * and back online.
+ */
+
+/*?
+ * Text: "%s: A channel path group could not be established\n"
+ * Severity: Warning
+ * Parameter:
+ * @1: bus ID of the DASD
+ * Description:
+ * Initialization of a DASD did not complete because a channel path group
+ * could not be established.
+ * User action:
+ * Make sure that the DASD is working correctly, then try again to set it
+ * online. If initialization still fails, reboot.
+ */
+
+/*?
+ * Text: "%s: The DASD is not operating in multipath mode\n"
+ * Severity: Informational
+ * Parameter:
+ * @1: bus ID of the DASD
+ * Description:
+ * The DASD channel path group could not be configured to use multipath mode.
+ * This might negatively affect I/O performance on this DASD.
+ * User action:
+ * Make sure that the DASD is working correctly, then try again to set it
+ * online. If initialization still fails, reboot.
+ */
+
+/*?
+ * Text: "%s: Detecting the DASD disk layout failed because of an I/O error\n"
+ * Severity: Error
+ * Parameter:
+ * @1: bus ID of the DASD
+ * Description:
+ * The disk layout of the DASD could not be detected because of an unexpected
+ * I/O error. The DASD device driver treats the device like an unformatted DASD,
+ * and partitions on the device are not accessible.
+ * User action:
+ * If the DASD is formatted, make sure that the DASD is working correctly,
+ * then set it offline and back online. If the DASD is unformatted, format the
+ * DASD, for example, with dasdfmt.
+ * ATTENTION: Formatting irreversibly destroys all data on the DASD.
+ */
diff --git a/Documentation/kmsg/s390/setup b/Documentation/kmsg/s390/setup
index 8a7a13a51e76..77158b9f3b8e 100644
--- a/Documentation/kmsg/s390/setup
+++ b/Documentation/kmsg/s390/setup
@@ -142,15 +142,6 @@
*/
/*?
- * Text: "Linux is running under KVM in 64-bit mode\n"
- * Severity: Informational
- * Description:
- * The 64-bit kernel detected that it is running under the KVM hypervisor.
- * User action:
- * None.
- */
-
-/*?
* Text: "Defining the Linux kernel NSS failed with rc=%d\n"
* Severity: Error
* Parameter:
@@ -185,3 +176,6 @@
* For other return codes, see the help and message documentation for
* the CP SAVESYS command.
*/
+
+/*? Text: "Linux is running under KVM in 64-bit mode\n" */
+
diff --git a/Documentation/kmsg/s390/vmur b/Documentation/kmsg/s390/vmur
index 208b90aa6420..52e7db25367e 100644
--- a/Documentation/kmsg/s390/vmur
+++ b/Documentation/kmsg/s390/vmur
@@ -37,11 +37,10 @@
* @1: bus ID of the unit record device
* @1: z/VM virtual unit record device driver
* Description:
- * A request to suspend a unit record device being currently in use is
- * rejected.
+ * Linux cannot be suspended while a unit record device is in use.
* User action:
- * Terminate applications working on z/VM spool files queues (e.g. the
- * vmur user space tool) and then try to suspend the system again.
+ * Stop all applications that work on z/VM spool file queues, for example, the
+ * vmur tool. Then try again to suspend Linux.
*/
/*? Text: "%s loaded.\n" */
diff --git a/Documentation/kmsg/s390/zfcp b/Documentation/kmsg/s390/zfcp
index 3d7dbe9f0a8c..b29f504c3d2c 100644
--- a/Documentation/kmsg/s390/zfcp
+++ b/Documentation/kmsg/s390/zfcp
@@ -844,3 +844,22 @@
* adapter. Check your SAN setup and consider reducing the number of ports
* visible to the FCP adapter by using more restrictive zoning in the SAN.
*/
+
+/*?
+ * Text: "%s: A port opened with WWPN 0x%016Lx returned data that identifies it as WWPN 0x%016Lx\n"
+ * Severity: Warning
+ * Parameter:
+ * @1: bus ID of the zfcp device
+ * @2: expected WWPN
+ * @3: reported WWPN
+ * Description:
+ * A remote port was opened successfully, but it reported an
+ * unexpected WWPN in the returned port login (PLOGI) data. This
+ * condition might have been caused by a change applied to the SAN
+ * configuration while the port was being opened.
+ * User action:
+ * If this condition is only temporary and access to the remote port
+ * is possible, no action is required. If the condition persists,
+ * identify the storage system with the specified WWPN and contact the
+ * support organization of the storage system.
+ */
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index e98a24a964dc..8bb07a2198ea 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -649,6 +649,7 @@ struct qeth_card_options {
int performance_stats;
int rx_sg_cb;
enum qeth_ipa_isolation_modes isolation;
+ int sniffer;
};
/*
@@ -737,6 +738,7 @@ struct qeth_card {
struct qeth_discipline discipline;
atomic_t force_alloc_skb;
struct service_level qeth_service_level;
+ struct qdio_ssqd_desc ssqd;
};
struct qeth_card_list_struct {
@@ -812,7 +814,8 @@ int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *,
struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *,
enum qeth_ipa_cmds, enum qeth_prot_versions);
int qeth_query_setadapterparms(struct qeth_card *);
-int qeth_check_qdio_errors(struct qdio_buffer *, unsigned int, const char *);
+int qeth_check_qdio_errors(struct qeth_card *, struct qdio_buffer *,
+ unsigned int, const char *);
void qeth_queue_input_buffer(struct qeth_card *, int);
struct sk_buff *qeth_core_get_next_skb(struct qeth_card *,
struct qdio_buffer *, struct qdio_buffer_element **, int *,
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index a391ca261dfb..5b62a90c7085 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -269,6 +269,7 @@ int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt)
card->qdio.init_pool.buf_count = bufcnt;
return qeth_alloc_buffer_pool(card);
}
+EXPORT_SYMBOL_GPL(qeth_realloc_buffer_pool);
int qeth_set_large_send(struct qeth_card *card,
enum qeth_large_send_types type)
@@ -385,8 +386,10 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
if (IS_IPA(iob->data)) {
cmd = (struct qeth_ipa_cmd *) PDU_ENCAPSULATION(iob->data);
if (IS_IPA_REPLY(cmd)) {
- if (cmd->hdr.command < IPA_CMD_SETCCID ||
- cmd->hdr.command > IPA_CMD_MODCCID)
+ if (cmd->hdr.command != IPA_CMD_SETCCID &&
+ cmd->hdr.command != IPA_CMD_DELCCID &&
+ cmd->hdr.command != IPA_CMD_MODCCID &&
+ cmd->hdr.command != IPA_CMD_SET_DIAG_ASS)
qeth_issue_ipa_msg(cmd,
cmd->hdr.return_code, card);
return cmd;
@@ -1135,11 +1138,6 @@ static int qeth_setup_card(struct qeth_card *card)
card->thread_running_mask = 0;
INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread);
INIT_LIST_HEAD(&card->ip_list);
- card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
- if (!card->ip_tbd_list) {
- QETH_DBF_TEXT(SETUP, 0, "iptbdnom");
- return -ENOMEM;
- }
INIT_LIST_HEAD(card->ip_tbd_list);
INIT_LIST_HEAD(&card->cmd_waiter_list);
init_waitqueue_head(&card->wait_q);
@@ -1173,21 +1171,30 @@ static struct qeth_card *qeth_alloc_card(void)
QETH_DBF_TEXT(SETUP, 2, "alloccrd");
card = kzalloc(sizeof(struct qeth_card), GFP_DMA|GFP_KERNEL);
if (!card)
- return NULL;
+ goto out;
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
- if (qeth_setup_channel(&card->read)) {
- kfree(card);
- return NULL;
- }
- if (qeth_setup_channel(&card->write)) {
- qeth_clean_channel(&card->read);
- kfree(card);
- return NULL;
+ card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+ if (!card->ip_tbd_list) {
+ QETH_DBF_TEXT(SETUP, 0, "iptbdnom");
+ goto out_card;
}
+ if (qeth_setup_channel(&card->read))
+ goto out_ip;
+ if (qeth_setup_channel(&card->write))
+ goto out_channel;
card->options.layer2 = -1;
card->qeth_service_level.seq_print = qeth_core_sl_print;
register_service_level(&card->qeth_service_level);
return card;
+
+out_channel:
+ qeth_clean_channel(&card->read);
+out_ip:
+ kfree(card->ip_tbd_list);
+out_card:
+ kfree(card);
+out:
+ return NULL;
}
static int qeth_determine_card_type(struct qeth_card *card)
@@ -2608,8 +2615,8 @@ int qeth_query_setadapterparms(struct qeth_card *card)
}
EXPORT_SYMBOL_GPL(qeth_query_setadapterparms);
-int qeth_check_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,
- const char *dbftext)
+int qeth_check_qdio_errors(struct qeth_card *card, struct qdio_buffer *buf,
+ unsigned int qdio_error, const char *dbftext)
{
if (qdio_error) {
QETH_DBF_TEXT(TRACE, 2, dbftext);
@@ -2619,7 +2626,11 @@ int qeth_check_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,
QETH_DBF_TEXT_(QERR, 2, " F14=%02X",
buf->element[14].flags & 0xff);
QETH_DBF_TEXT_(QERR, 2, " qerr=%X", qdio_error);
- return 1;
+ if ((buf->element[15].flags & 0xff) == 0x12) {
+ card->stats.rx_dropped++;
+ return 0;
+ } else
+ return 1;
}
return 0;
}
@@ -2702,7 +2713,7 @@ static int qeth_handle_send_error(struct qeth_card *card,
qdio_err = 1;
}
}
- qeth_check_qdio_errors(buffer->buffer, qdio_err, "qouterr");
+ qeth_check_qdio_errors(card, buffer->buffer, qdio_err, "qouterr");
if (!qdio_err)
return QETH_SEND_ERROR_NONE;
@@ -3544,6 +3555,7 @@ void qeth_tx_timeout(struct net_device *dev)
{
struct qeth_card *card;
+ QETH_DBF_TEXT(TRACE, 4, "txtimeo");
card = dev->ml_priv;
card->stats.tx_errors++;
qeth_schedule_recovery(card);
@@ -3882,9 +3894,7 @@ static int qeth_core_driver_group(const char *buf, struct device *root_dev,
int qeth_core_hardsetup_card(struct qeth_card *card)
{
- struct qdio_ssqd_desc *ssqd;
int retries = 3;
- int mpno = 0;
int rc;
QETH_DBF_TEXT(SETUP, 2, "hrdsetup");
@@ -3911,31 +3921,6 @@ retry:
else
goto retry;
}
-
- rc = qeth_get_unitaddr(card);
- if (rc) {
- QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
- return rc;
- }
-
- ssqd = kmalloc(sizeof(struct qdio_ssqd_desc), GFP_KERNEL);
- if (!ssqd) {
- rc = -ENOMEM;
- goto out;
- }
- rc = qdio_get_ssqd_desc(CARD_DDEV(card), ssqd);
- if (rc == 0)
- mpno = ssqd->pcnt;
- kfree(ssqd);
-
- if (mpno)
- mpno = min(mpno - 1, QETH_MAX_PORTNO);
- if (card->info.portno > mpno) {
- QETH_DBF_MESSAGE(2, "Device %s does not offer port number %d"
- "\n.", CARD_BUS_ID(card), card->info.portno);
- rc = -ENODEV;
- goto out;
- }
qeth_init_tokens(card);
qeth_init_func_level(card);
rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb);
@@ -4019,7 +4004,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
struct qdio_buffer_element *element = *__element;
int offset = *__offset;
struct sk_buff *skb = NULL;
- int skb_len;
+ int skb_len = 0;
void *data_ptr;
int data_len;
int headroom = 0;
@@ -4038,20 +4023,24 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
*hdr = element->addr + offset;
offset += sizeof(struct qeth_hdr);
- if (card->options.layer2) {
- if (card->info.type == QETH_CARD_TYPE_OSN) {
- skb_len = (*hdr)->hdr.osn.pdu_length;
- headroom = sizeof(struct qeth_hdr);
- } else {
- skb_len = (*hdr)->hdr.l2.pkt_length;
- }
- } else {
+ switch ((*hdr)->hdr.l2.id) {
+ case QETH_HEADER_TYPE_LAYER2:
+ skb_len = (*hdr)->hdr.l2.pkt_length;
+ break;
+ case QETH_HEADER_TYPE_LAYER3:
skb_len = (*hdr)->hdr.l3.length;
if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) ||
(card->info.link_type == QETH_LINK_TYPE_HSTR))
headroom = TR_HLEN;
else
headroom = ETH_HLEN;
+ break;
+ case QETH_HEADER_TYPE_OSN:
+ skb_len = (*hdr)->hdr.osn.pdu_length;
+ headroom = sizeof(struct qeth_hdr);
+ break;
+ default:
+ break;
}
if (!skb_len)
@@ -4206,6 +4195,33 @@ void qeth_core_free_discipline(struct qeth_card *card)
card->discipline.ccwgdriver = NULL;
}
+static void qeth_determine_capabilities(struct qeth_card *card)
+{
+ int rc;
+
+ QETH_DBF_TEXT(SETUP, 2, "detcapab");
+ rc = ccw_device_set_online(CARD_DDEV(card));
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
+ goto out;
+ }
+
+ rc = qeth_get_unitaddr(card);
+ if (rc) {
+ QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
+ goto out_offline;
+ }
+
+ rc = qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd);
+ if (rc)
+ QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
+
+out_offline:
+ ccw_device_set_offline(CARD_DDEV(card));
+out:
+ return;
+}
+
static int qeth_core_probe_device(struct ccwgroup_device *gdev)
{
struct qeth_card *card;
@@ -4271,6 +4287,8 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
write_lock_irqsave(&qeth_core_card_list.rwlock, flags);
list_add_tail(&card->list, &qeth_core_card_list.list);
write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
+
+ qeth_determine_capabilities(card);
return 0;
err_card:
diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h
index 52c03438dbec..307d55e6b4d6 100644
--- a/drivers/s390/net/qeth_core_mpc.h
+++ b/drivers/s390/net/qeth_core_mpc.h
@@ -156,6 +156,8 @@ enum qeth_ipa_return_codes {
IPA_RC_IP_TABLE_FULL = 0x0002,
IPA_RC_UNKNOWN_ERROR = 0x0003,
IPA_RC_UNSUPPORTED_COMMAND = 0x0004,
+ IPA_RC_TRACE_ALREADY_ACTIVE = 0x0005,
+ IPA_RC_INVALID_FORMAT = 0x0006,
IPA_RC_DUP_IPV6_REMOTE = 0x0008,
IPA_RC_DUP_IPV6_HOME = 0x0010,
IPA_RC_UNREGISTERED_ADDR = 0x0011,
@@ -196,6 +198,11 @@ enum qeth_ipa_return_codes {
IPA_RC_INVALID_IP_VERSION2 = 0xf001,
IPA_RC_FFFF = 0xffff
};
+/* for DELIP */
+#define IPA_RC_IP_ADDRESS_NOT_DEFINED IPA_RC_PRIMARY_ALREADY_DEFINED
+/* for SET_DIAGNOSTIC_ASSIST */
+#define IPA_RC_INVALID_SUBCMD IPA_RC_IP_TABLE_FULL
+#define IPA_RC_HARDWARE_AUTH_ERROR IPA_RC_UNKNOWN_ERROR
/* IPA function flags; each flag marks availability of respective function */
enum qeth_ipa_funcs {
@@ -246,6 +253,7 @@ enum qeth_ipa_setadp_cmd {
IPA_SETADP_SET_SNMP_CONTROL = 0x00000200L,
IPA_SETADP_QUERY_CARD_INFO = 0x00000400L,
IPA_SETADP_SET_PROMISC_MODE = 0x00000800L,
+ IPA_SETADP_SET_DIAG_ASSIST = 0x00002000L,
IPA_SETADP_SET_ACCESS_CONTROL = 0x00010000L,
};
enum qeth_ipa_mac_ops {
@@ -424,6 +432,40 @@ struct qeth_create_destroy_address {
__u8 unique_id[8];
} __attribute__ ((packed));
+/* SET DIAGNOSTIC ASSIST IPA Command: *************************************/
+
+enum qeth_diags_cmds {
+ QETH_DIAGS_CMD_QUERY = 0x0001,
+ QETH_DIAGS_CMD_TRAP = 0x0002,
+ QETH_DIAGS_CMD_TRACE = 0x0004,
+ QETH_DIAGS_CMD_NOLOG = 0x0008,
+ QETH_DIAGS_CMD_DUMP = 0x0010,
+};
+
+enum qeth_diags_trace_types {
+ QETH_DIAGS_TYPE_HIPERSOCKET = 0x02,
+};
+
+enum qeth_diags_trace_cmds {
+ QETH_DIAGS_CMD_TRACE_ENABLE = 0x0001,
+ QETH_DIAGS_CMD_TRACE_DISABLE = 0x0002,
+ QETH_DIAGS_CMD_TRACE_MODIFY = 0x0004,
+ QETH_DIAGS_CMD_TRACE_REPLACE = 0x0008,
+ QETH_DIAGS_CMD_TRACE_QUERY = 0x0010,
+};
+
+struct qeth_ipacmd_diagass {
+ __u32 host_tod2;
+ __u32:32;
+ __u16 subcmd_len;
+ __u16:16;
+ __u32 subcmd;
+ __u8 type;
+ __u8 action;
+ __u16 options;
+ __u32:32;
+} __attribute__ ((packed));
+
/* Header for each IPA command */
struct qeth_ipacmd_hdr {
__u8 command;
@@ -452,6 +494,7 @@ struct qeth_ipa_cmd {
struct qeth_create_destroy_address create_destroy_addr;
struct qeth_ipacmd_setadpparms setadapterparms;
struct qeth_set_routing setrtg;
+ struct qeth_ipacmd_diagass diagass;
} data;
} __attribute__ ((packed));
@@ -469,7 +512,6 @@ enum qeth_ipa_arp_return_codes {
QETH_IPA_ARP_RC_Q_NO_DATA = 0x0008,
};
-
extern char *qeth_get_ipa_msg(enum qeth_ipa_return_codes rc);
extern char *qeth_get_ipa_cmd_name(enum qeth_ipa_cmds cmd);
diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c
index f2358a75ed0c..3bb767075a6c 100644
--- a/drivers/s390/net/qeth_core_sys.c
+++ b/drivers/s390/net/qeth_core_sys.c
@@ -118,7 +118,7 @@ static ssize_t qeth_dev_portno_store(struct device *dev,
{
struct qeth_card *card = dev_get_drvdata(dev);
char *tmp;
- unsigned int portno;
+ unsigned int portno, limit;
if (!card)
return -EINVAL;
@@ -128,9 +128,11 @@ static ssize_t qeth_dev_portno_store(struct device *dev,
return -EPERM;
portno = simple_strtoul(buf, &tmp, 16);
- if (portno > QETH_MAX_PORTNO) {
+ if (portno > QETH_MAX_PORTNO)
+ return -EINVAL;
+ limit = (card->ssqd.pcnt ? card->ssqd.pcnt - 1 : card->ssqd.pcnt);
+ if (portno > limit)
return -EINVAL;
- }
card->info.portno = portno;
return count;
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 5935168fa631..99316eb566dc 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -781,7 +781,8 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
index = i % QDIO_MAX_BUFFERS_PER_Q;
buffer = &card->qdio.in_q->bufs[index];
if (!(qdio_err &&
- qeth_check_qdio_errors(buffer->buffer, qdio_err, "qinerr")))
+ qeth_check_qdio_errors(card, buffer->buffer, qdio_err,
+ "qinerr")))
qeth_l2_process_inbound_buffer(card, buffer, index);
/* clear buffer and give back to hardware */
qeth_put_buffer_pool_entry(card, buffer->pool_entry);
diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h
index 9f143c83bba3..9946dbc4f4df 100644
--- a/drivers/s390/net/qeth_l3.h
+++ b/drivers/s390/net/qeth_l3.h
@@ -13,6 +13,8 @@
#include "qeth_core.h"
+#define QETH_SNIFF_AVAIL 0x0008
+
struct qeth_ipaddr {
struct list_head entry;
enum qeth_ip_types type;
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 06c5130cff9f..2570d0266b05 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -216,6 +216,8 @@ static int __qeth_l3_insert_ip_todo(struct qeth_card *card,
struct qeth_ipaddr *tmp, *t;
int found = 0;
+ if (card->options.sniffer)
+ return 0;
list_for_each_entry_safe(tmp, t, card->ip_tbd_list, entry) {
if ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) &&
(tmp->type == QETH_IP_TYPE_DEL_ALL_MC))
@@ -431,6 +433,8 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 2, "sdiplist");
QETH_DBF_HEX(TRACE, 2, &card, sizeof(void *));
+ if (card->options.sniffer)
+ return;
spin_lock_irqsave(&card->ip_lock, flags);
tbd_list = card->ip_tbd_list;
card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
@@ -469,7 +473,7 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
spin_unlock_irqrestore(&card->ip_lock, flags);
rc = qeth_l3_deregister_addr_entry(card, addr);
spin_lock_irqsave(&card->ip_lock, flags);
- if (!rc || (rc == IPA_RC_PRIMARY_ALREADY_DEFINED))
+ if (!rc || (rc == IPA_RC_IP_ADDRESS_NOT_DEFINED))
kfree(addr);
else
list_add_tail(&addr->entry, &card->ip_list);
@@ -487,6 +491,8 @@ static void qeth_l3_clear_ip_list(struct qeth_card *card, int clean,
unsigned long flags;
QETH_DBF_TEXT(TRACE, 4, "clearip");
+ if (recover && card->options.sniffer)
+ return;
spin_lock_irqsave(&card->ip_lock, flags);
/* clear todo list */
list_for_each_entry_safe(addr, tmp, card->ip_tbd_list, entry) {
@@ -1619,6 +1625,76 @@ static int qeth_l3_get_unique_id(struct qeth_card *card)
return rc;
}
+static int
+qeth_diags_trace_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ __u16 rc;
+
+ QETH_DBF_TEXT(SETUP, 2, "diastrcb");
+
+ cmd = (struct qeth_ipa_cmd *)data;
+ rc = cmd->hdr.return_code;
+ if (rc) {
+ QETH_DBF_TEXT_(TRACE, 2, "dxter%x", rc);
+ if (cmd->data.diagass.action == QETH_DIAGS_CMD_TRACE_ENABLE) {
+ switch (rc) {
+ case IPA_RC_HARDWARE_AUTH_ERROR:
+ dev_warn(&card->gdev->dev, "The device is not "
+ "authorized to run as a HiperSockets "
+ "network traffic analyzer\n");
+ break;
+ case IPA_RC_TRACE_ALREADY_ACTIVE:
+ dev_warn(&card->gdev->dev, "A HiperSockets "
+ "network traffic analyzer is already "
+ "active in the HiperSockets LAN\n");
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+ }
+
+ switch (cmd->data.diagass.action) {
+ case QETH_DIAGS_CMD_TRACE_QUERY:
+ break;
+ case QETH_DIAGS_CMD_TRACE_DISABLE:
+ card->info.promisc_mode = SET_PROMISC_MODE_OFF;
+ dev_info(&card->gdev->dev, "The HiperSockets network traffic "
+ "analyzer is deactivated\n");
+ break;
+ case QETH_DIAGS_CMD_TRACE_ENABLE:
+ card->info.promisc_mode = SET_PROMISC_MODE_ON;
+ dev_info(&card->gdev->dev, "The HiperSockets network traffic "
+ "analyzer is activated\n");
+ break;
+ default:
+ QETH_DBF_MESSAGE(2, "Unknown sniffer action (0x%04x) on %s\n",
+ cmd->data.diagass.action, QETH_CARD_IFNAME(card));
+ }
+
+ return 0;
+}
+
+static int
+qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_DBF_TEXT(SETUP, 2, "diagtrac");
+
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.diagass.subcmd_len = 16;
+ cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRACE;
+ cmd->data.diagass.type = QETH_DIAGS_TYPE_HIPERSOCKET;
+ cmd->data.diagass.action = diags_cmd;
+ return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL);
+}
+
static void qeth_l3_get_mac_for_ipm(__u32 ipm, char *mac,
struct net_device *dev)
{
@@ -1896,7 +1972,10 @@ static inline __u16 qeth_l3_rebuild_skb(struct qeth_card *card,
case QETH_CAST_ANYCAST:
case QETH_CAST_NOCAST:
default:
- skb->pkt_type = PACKET_HOST;
+ if (card->options.sniffer)
+ skb->pkt_type = PACKET_OTHERHOST;
+ else
+ skb->pkt_type = PACKET_HOST;
memcpy(tg_addr, card->dev->dev_addr,
card->dev->addr_len);
}
@@ -1952,7 +2031,6 @@ static void qeth_l3_process_inbound_buffer(struct qeth_card *card,
int offset;
__u16 vlan_tag = 0;
unsigned int len;
-
/* get first element of current buffer */
element = (struct qdio_buffer_element *)&buf->buffer->element[0];
offset = 0;
@@ -1971,7 +2049,7 @@ static void qeth_l3_process_inbound_buffer(struct qeth_card *card,
case QETH_HEADER_TYPE_LAYER3:
vlan_tag = qeth_l3_rebuild_skb(card, skb, hdr);
len = skb->len;
- if (vlan_tag)
+ if (vlan_tag && !card->options.sniffer)
if (card->vlangrp)
vlan_hwaccel_rx(skb, card->vlangrp,
vlan_tag);
@@ -1982,6 +2060,16 @@ static void qeth_l3_process_inbound_buffer(struct qeth_card *card,
else
netif_rx(skb);
break;
+ case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+ if (card->options.checksum_type == NO_CHECKSUMMING)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb->ip_summed = CHECKSUM_NONE;
+ len = skb->len;
+ netif_receive_skb(skb);
+ break;
default:
dev_kfree_skb_any(skb);
QETH_DBF_TEXT(TRACE, 3, "inbunkno");
@@ -2063,6 +2151,9 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
qeth_set_allowed_threads(card, 0, 1);
+ if (card->options.sniffer &&
+ (card->info.promisc_mode == SET_PROMISC_MODE_ON))
+ qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
if (card->read.state == CH_STATE_UP &&
card->write.state == CH_STATE_UP &&
(card->state == CARD_STATE_UP)) {
@@ -2107,6 +2198,36 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
return rc;
}
+/*
+ * test for and Switch promiscuous mode (on or off)
+ * either for guestlan or HiperSocket Sniffer
+ */
+static void
+qeth_l3_handle_promisc_mode(struct qeth_card *card)
+{
+ struct net_device *dev = card->dev;
+
+ if (((dev->flags & IFF_PROMISC) &&
+ (card->info.promisc_mode == SET_PROMISC_MODE_ON)) ||
+ (!(dev->flags & IFF_PROMISC) &&
+ (card->info.promisc_mode == SET_PROMISC_MODE_OFF)))
+ return;
+
+ if (card->info.guestlan) { /* Guestlan trace */
+ if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
+ qeth_setadp_promisc_mode(card);
+ } else if (card->options.sniffer && /* HiperSockets trace */
+ qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) {
+ if (dev->flags & IFF_PROMISC) {
+ QETH_DBF_TEXT(TRACE, 3, "+promisc");
+ qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_ENABLE);
+ } else {
+ QETH_DBF_TEXT(TRACE, 3, "-promisc");
+ qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
+ }
+ }
+}
+
static void qeth_l3_set_multicast_list(struct net_device *dev)
{
struct qeth_card *card = dev->ml_priv;
@@ -2115,15 +2236,17 @@ static void qeth_l3_set_multicast_list(struct net_device *dev)
if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
(card->state != CARD_STATE_UP))
return;
- qeth_l3_delete_mc_addresses(card);
- qeth_l3_add_multicast_ipv4(card);
+ if (!card->options.sniffer) {
+ qeth_l3_delete_mc_addresses(card);
+ qeth_l3_add_multicast_ipv4(card);
#ifdef CONFIG_QETH_IPV6
- qeth_l3_add_multicast_ipv6(card);
+ qeth_l3_add_multicast_ipv6(card);
#endif
- qeth_l3_set_ip_addr_list(card);
- if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
- return;
- qeth_setadp_promisc_mode(card);
+ qeth_l3_set_ip_addr_list(card);
+ if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
+ return;
+ }
+ qeth_l3_handle_promisc_mode(card);
}
static const char *qeth_l3_arp_get_error_cause(int *rc)
@@ -2705,8 +2828,9 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
int nr_frags;
if ((card->info.type == QETH_CARD_TYPE_IQD) &&
- (skb->protocol != htons(ETH_P_IPV6)) &&
- (skb->protocol != htons(ETH_P_IP)))
+ (((skb->protocol != htons(ETH_P_IPV6)) &&
+ (skb->protocol != htons(ETH_P_IP))) ||
+ card->options.sniffer))
goto tx_drop;
if ((card->state != CARD_STATE_UP) || !card->lan_online) {
@@ -3094,7 +3218,7 @@ static void qeth_l3_qdio_input_handler(struct ccw_device *ccwdev,
index = i % QDIO_MAX_BUFFERS_PER_Q;
buffer = &card->qdio.in_q->bufs[index];
if (!(qdio_err &&
- qeth_check_qdio_errors(buffer->buffer,
+ qeth_check_qdio_errors(card, buffer->buffer,
qdio_err, "qinerr")))
qeth_l3_process_inbound_buffer(card, buffer, index);
/* clear buffer and give back to hardware */
@@ -3201,20 +3325,22 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
goto out_remove;
} else
card->lan_online = 1;
- qeth_set_large_send(card, card->options.large_send);
rc = qeth_l3_setadapter_parms(card);
if (rc)
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
- rc = qeth_l3_start_ipassists(card);
- if (rc)
- QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
- rc = qeth_l3_setrouting_v4(card);
- if (rc)
- QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc);
- rc = qeth_l3_setrouting_v6(card);
- if (rc)
- QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
+ if (!card->options.sniffer) {
+ rc = qeth_l3_start_ipassists(card);
+ if (rc)
+ QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
+ qeth_set_large_send(card, card->options.large_send);
+ rc = qeth_l3_setrouting_v4(card);
+ if (rc)
+ QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc);
+ rc = qeth_l3_setrouting_v6(card);
+ if (rc)
+ QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
+ }
netif_tx_disable(card->dev);
rc = qeth_init_qdio_queues(card);
diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c
index c144b9924d52..b570df5d88cd 100644
--- a/drivers/s390/net/qeth_l3_sys.c
+++ b/drivers/s390/net/qeth_l3_sys.c
@@ -318,6 +318,61 @@ static ssize_t qeth_l3_dev_checksum_store(struct device *dev,
static DEVICE_ATTR(checksumming, 0644, qeth_l3_dev_checksum_show,
qeth_l3_dev_checksum_store);
+static ssize_t qeth_l3_dev_sniffer_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->options.sniffer ? 1 : 0);
+}
+
+static ssize_t qeth_l3_dev_sniffer_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ int ret;
+ unsigned long i;
+
+ if (!card)
+ return -EINVAL;
+
+ if (card->info.type != QETH_CARD_TYPE_IQD)
+ return -EPERM;
+
+ if ((card->state != CARD_STATE_DOWN) &&
+ (card->state != CARD_STATE_RECOVER))
+ return -EPERM;
+
+ ret = strict_strtoul(buf, 16, &i);
+ if (ret)
+ return -EINVAL;
+ switch (i) {
+ case 0:
+ card->options.sniffer = i;
+ break;
+ case 1:
+ ret = qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd);
+ if (card->ssqd.qdioac2 & QETH_SNIFF_AVAIL) {
+ card->options.sniffer = i;
+ if (card->qdio.init_pool.buf_count !=
+ QETH_IN_BUF_COUNT_MAX)
+ qeth_realloc_buffer_pool(card,
+ QETH_IN_BUF_COUNT_MAX);
+ break;
+ } else
+ return -EPERM;
+ default: /* fall through */
+ return -EINVAL;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show,
+ qeth_l3_dev_sniffer_store);
+
static struct attribute *qeth_l3_device_attrs[] = {
&dev_attr_route4.attr,
&dev_attr_route6.attr,
@@ -325,6 +380,7 @@ static struct attribute *qeth_l3_device_attrs[] = {
&dev_attr_broadcast_mode.attr,
&dev_attr_canonical_macaddr.attr,
&dev_attr_checksumming.attr,
+ &dev_attr_sniffer.attr,
NULL,
};
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index ccb26724dc53..b5b8ae336a80 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -1183,6 +1183,7 @@ typedef struct {
#define PCI_DEVICE_ID_ZEPHYR_DCSP 0xfe12
#define PCI_VENDOR_ID_SERVERENGINE 0x19a2
#define PCI_DEVICE_ID_TIGERSHARK 0x0704
+#define PCI_DEVICE_ID_RAYWIRE 0x0214
#define JEDEC_ID_ADDRESS 0x0080001c
#define FIREFLY_JEDEC_ID 0x1ACC
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index f913f1e93635..80c352a264e1 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -7969,6 +7969,8 @@ static struct pci_device_id lpfc_id_table[] = {
PCI_ANY_ID, PCI_ANY_ID, },
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_S,
PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_RAYWIRE,
+ PCI_ANY_ID, PCI_ANY_ID, },
{PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TIGERSHARK,
PCI_ANY_ID, PCI_ANY_ID, },
{ 0 }
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index cca8e4ab0372..cb2eca4c26d8 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -377,6 +377,24 @@ qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
return ptr + sizeof(struct qla2xxx_mq_chain);
}
+static void
+qla2xxx_dump_post_process(scsi_qla_host_t *vha, int rval)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ if (rval != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Failed to dump firmware (%x)!!!\n", rval);
+ ha->fw_dumped = 0;
+ } else {
+ qla_printk(KERN_INFO, ha,
+ "Firmware dump saved to temp buffer (%ld/%p).\n",
+ vha->host_no, ha->fw_dump);
+ ha->fw_dumped = 1;
+ qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP);
+ }
+}
+
/**
* qla2300_fw_dump() - Dumps binary data from the 2300 firmware.
* @ha: HA context
@@ -530,17 +548,7 @@ qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
if (rval == QLA_SUCCESS)
qla2xxx_copy_queues(ha, nxt);
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla2300_fw_dump_failed:
if (!hardware_locked)
@@ -737,17 +745,7 @@ qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
if (rval == QLA_SUCCESS)
qla2xxx_copy_queues(ha, &fw->risc_ram[cnt]);
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla2100_fw_dump_failed:
if (!hardware_locked)
@@ -984,17 +982,7 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
qla24xx_copy_eft(ha, nxt);
qla24xx_fw_dump_failed_0:
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla24xx_fw_dump_failed:
if (!hardware_locked)
@@ -1305,17 +1293,7 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
}
qla25xx_fw_dump_failed_0:
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla25xx_fw_dump_failed:
if (!hardware_locked)
@@ -1628,17 +1606,7 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
}
qla81xx_fw_dump_failed_0:
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla81xx_fw_dump_failed:
if (!hardware_locked)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 215061861794..6b9bf23c7735 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -2123,6 +2123,7 @@ enum qla_work_type {
QLA_EVT_ASYNC_LOGIN_DONE,
QLA_EVT_ASYNC_LOGOUT,
QLA_EVT_ASYNC_LOGOUT_DONE,
+ QLA_EVT_UEVENT,
};
@@ -2146,6 +2147,10 @@ struct qla_work_evt {
#define QLA_LOGIO_LOGIN_RETRIED BIT_0
u16 data[2];
} logio;
+ struct {
+ u32 code;
+#define QLA_UEVENT_CODE_FW_DUMP 0
+ } uevent;
} u;
};
@@ -2435,11 +2440,11 @@ struct qla_hw_data {
dma_addr_t edc_data_dma;
uint16_t edc_data_len;
-#define XGMAC_DATA_SIZE PAGE_SIZE
+#define XGMAC_DATA_SIZE 4096
void *xgmac_data;
dma_addr_t xgmac_data_dma;
-#define DCBX_TLV_DATA_SIZE PAGE_SIZE
+#define DCBX_TLV_DATA_SIZE 4096
void *dcbx_tlv;
dma_addr_t dcbx_tlv_dma;
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index f3d1d1afa95b..e21851358509 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -92,6 +92,7 @@ extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *,
uint16_t *);
extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *,
fc_port_t *, uint16_t *);
+extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32);
extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *);
@@ -246,7 +247,7 @@ qla2x00_get_id_list(scsi_qla_host_t *, void *, dma_addr_t, uint16_t *);
extern int
qla2x00_get_resource_cnts(scsi_qla_host_t *, uint16_t *, uint16_t *,
- uint16_t *, uint16_t *, uint16_t *);
+ uint16_t *, uint16_t *, uint16_t *, uint16_t *);
extern int
qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map);
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 9e3eaac25596..b74924b279ef 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -277,7 +277,6 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
vha->marker_needed = 0;
ha->isp_abort_cnt = 0;
ha->beacon_blink_led = 0;
- set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
set_bit(0, ha->req_qid_map);
set_bit(0, ha->rsp_qid_map);
@@ -1203,7 +1202,7 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
}
qla2x00_get_resource_cnts(vha, NULL,
&ha->fw_xcb_count, NULL, NULL,
- &ha->max_npiv_vports);
+ &ha->max_npiv_vports, NULL);
if (!fw_major_version && ql2xallocfwdump)
qla2x00_alloc_fw_dump(vha);
@@ -3573,6 +3572,15 @@ qla2x00_abort_isp(scsi_qla_host_t *vha)
ha->isp_abort_cnt = 0;
clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+ if (IS_QLA81XX(ha))
+ qla2x00_get_fw_version(vha,
+ &ha->fw_major_version,
+ &ha->fw_minor_version,
+ &ha->fw_subminor_version,
+ &ha->fw_attributes, &ha->fw_memory_size,
+ ha->mpi_version, &ha->mpi_capabilities,
+ ha->phy_version);
+
if (ha->fce) {
ha->flags.fce_enabled = 1;
memset(ha->fce, 0,
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index b20a7169aac2..804987397b77 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -313,10 +313,11 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" };
char *link_speed;
uint16_t handle_cnt;
- uint16_t cnt;
+ uint16_t cnt, mbx;
uint32_t handles[5];
struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
uint32_t rscn_entry, host_pid;
uint8_t rscn_queue_index;
unsigned long flags;
@@ -395,9 +396,10 @@ skip_rio:
break;
case MBA_SYSTEM_ERR: /* System Error */
+ mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox7) : 0;
qla_printk(KERN_INFO, ha,
- "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n",
- mb[1], mb[2], mb[3]);
+ "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh "
+ "mbx7=%xh.\n", mb[1], mb[2], mb[3], mbx);
ha->isp_ops->fw_dump(vha, 1);
@@ -419,9 +421,10 @@ skip_rio:
break;
case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */
- DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n",
- vha->host_no));
- qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n");
+ DEBUG2(printk("scsi(%ld): ISP Request Transfer Error (%x).\n",
+ vha->host_no, mb[1]));
+ qla_printk(KERN_WARNING, ha,
+ "ISP Request Transfer Error (%x).\n", mb[1]);
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
@@ -485,10 +488,13 @@ skip_rio:
break;
case MBA_LOOP_DOWN: /* Loop Down Event */
+ mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox4) : 0;
DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN "
- "(%x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3]));
- qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x %x %x).\n",
- mb[1], mb[2], mb[3]);
+ "(%x %x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3],
+ mbx));
+ qla_printk(KERN_INFO, ha,
+ "LOOP DOWN detected (%x %x %x %x).\n", mb[1], mb[2], mb[3],
+ mbx);
if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
atomic_set(&vha->loop_state, LOOP_DOWN);
@@ -1347,16 +1353,22 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
sense_len = rsp_info_len = resid_len = fw_resid_len = 0;
if (IS_FWI2_CAPABLE(ha)) {
- sense_len = le32_to_cpu(sts24->sense_len);
- rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
- resid_len = le32_to_cpu(sts24->rsp_residual_count);
- fw_resid_len = le32_to_cpu(sts24->residual_len);
+ if (scsi_status & SS_SENSE_LEN_VALID)
+ sense_len = le32_to_cpu(sts24->sense_len);
+ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
+ rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
+ if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER))
+ resid_len = le32_to_cpu(sts24->rsp_residual_count);
+ if (comp_status == CS_DATA_UNDERRUN)
+ fw_resid_len = le32_to_cpu(sts24->residual_len);
rsp_info = sts24->data;
sense_data = sts24->data;
host_to_fcp_swap(sts24->data, sizeof(sts24->data));
} else {
- sense_len = le16_to_cpu(sts->req_sense_length);
- rsp_info_len = le16_to_cpu(sts->rsp_info_len);
+ if (scsi_status & SS_SENSE_LEN_VALID)
+ sense_len = le16_to_cpu(sts->req_sense_length);
+ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
+ rsp_info_len = le16_to_cpu(sts->rsp_info_len);
resid_len = le32_to_cpu(sts->residual_length);
rsp_info = sts->rsp_info;
sense_data = sts->req_sense_data;
@@ -1443,38 +1455,62 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
break;
case CS_DATA_UNDERRUN:
- resid = resid_len;
+ DEBUG2(printk(KERN_INFO
+ "scsi(%ld:%d:%d) UNDERRUN status detected 0x%x-0x%x. "
+ "resid=0x%x fw_resid=0x%x cdb=0x%x os_underflow=0x%x\n",
+ vha->host_no, cp->device->id, cp->device->lun, comp_status,
+ scsi_status, resid_len, fw_resid_len, cp->cmnd[0],
+ cp->underflow));
+
/* Use F/W calculated residual length. */
- if (IS_FWI2_CAPABLE(ha)) {
- if (!(scsi_status & SS_RESIDUAL_UNDER)) {
- lscsi_status = 0;
- } else if (resid != fw_resid_len) {
- scsi_status &= ~SS_RESIDUAL_UNDER;
- lscsi_status = 0;
+ resid = IS_FWI2_CAPABLE(ha) ? fw_resid_len : resid_len;
+ scsi_set_resid(cp, resid);
+ if (scsi_status & SS_RESIDUAL_UNDER) {
+ if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) {
+ DEBUG2(printk(
+ "scsi(%ld:%d:%d:%d) Dropped frame(s) "
+ "detected (%x of %x bytes)...residual "
+ "length mismatch...retrying command.\n",
+ vha->host_no, cp->device->channel,
+ cp->device->id, cp->device->lun, resid,
+ scsi_bufflen(cp)));
+
+ cp->result = DID_ERROR << 16 | lscsi_status;
+ break;
}
- resid = fw_resid_len;
- }
- if (scsi_status & SS_RESIDUAL_UNDER) {
- scsi_set_resid(cp, resid);
- } else {
- DEBUG2(printk(KERN_INFO
- "scsi(%ld:%d:%d) UNDERRUN status detected "
- "0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x "
- "os_underflow=0x%x\n", vha->host_no,
- cp->device->id, cp->device->lun, comp_status,
- scsi_status, resid_len, resid, cp->cmnd[0],
- cp->underflow));
+ if (!lscsi_status &&
+ ((unsigned)(scsi_bufflen(cp) - resid) <
+ cp->underflow)) {
+ qla_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): Mid-layer underflow "
+ "detected (%x of %x bytes)...returning "
+ "error status.\n", vha->host_no,
+ cp->device->channel, cp->device->id,
+ cp->device->lun, resid, scsi_bufflen(cp));
+
+ cp->result = DID_ERROR << 16;
+ break;
+ }
+ } else if (!lscsi_status) {
+ DEBUG2(printk(
+ "scsi(%ld:%d:%d:%d) Dropped frame(s) detected "
+ "(%x of %x bytes)...firmware reported underrun..."
+ "retrying command.\n", vha->host_no,
+ cp->device->channel, cp->device->id,
+ cp->device->lun, resid, scsi_bufflen(cp)));
+ cp->result = DID_ERROR << 16;
+ break;
}
+ cp->result = DID_OK << 16 | lscsi_status;
+
/*
* Check to see if SCSI Status is non zero. If so report SCSI
* Status.
*/
if (lscsi_status != 0) {
- cp->result = DID_OK << 16 | lscsi_status;
-
if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
DEBUG2(printk(KERN_INFO
"scsi(%ld): QUEUE FULL status detected "
@@ -1501,42 +1537,6 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
break;
qla2x00_handle_sense(sp, sense_data, sense_len, rsp);
- } else {
- /*
- * If RISC reports underrun and target does not report
- * it then we must have a lost frame, so tell upper
- * layer to retry it by reporting an error.
- */
- if (!(scsi_status & SS_RESIDUAL_UNDER)) {
- DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
- "frame(s) detected (%x of %x bytes)..."
- "retrying command.\n",
- vha->host_no, cp->device->channel,
- cp->device->id, cp->device->lun, resid,
- scsi_bufflen(cp)));
-
- scsi_set_resid(cp, resid);
- cp->result = DID_ERROR << 16;
- break;
- }
-
- /* Handle mid-layer underflow */
- if ((unsigned)(scsi_bufflen(cp) - resid) <
- cp->underflow) {
- qla_printk(KERN_INFO, ha,
- "scsi(%ld:%d:%d:%d): Mid-layer underflow "
- "detected (%x of %x bytes)...returning "
- "error status.\n", vha->host_no,
- cp->device->channel, cp->device->id,
- cp->device->lun, resid,
- scsi_bufflen(cp));
-
- cp->result = DID_ERROR << 16;
- break;
- }
-
- /* Everybody online, looking good... */
- cp->result = DID_OK << 16;
}
break;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index b6202fe118ac..05d595d9a7ef 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -2006,7 +2006,7 @@ qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma,
int
qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
uint16_t *orig_xchg_cnt, uint16_t *cur_iocb_cnt,
- uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports)
+ uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports, uint16_t *max_fcfs)
{
int rval;
mbx_cmd_t mc;
@@ -2017,6 +2017,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ if (IS_QLA81XX(vha->hw))
+ mcp->in_mb |= MBX_12;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
rval = qla2x00_mailbox_command(vha, mcp);
@@ -2027,9 +2029,10 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
vha->host_no, mcp->mb[0]));
} else {
DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x "
- "mb7=%x mb10=%x mb11=%x.\n", __func__, vha->host_no,
- mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[6], mcp->mb[7],
- mcp->mb[10], mcp->mb[11]));
+ "mb7=%x mb10=%x mb11=%x mb12=%x.\n", __func__,
+ vha->host_no, mcp->mb[1], mcp->mb[2], mcp->mb[3],
+ mcp->mb[6], mcp->mb[7], mcp->mb[10], mcp->mb[11],
+ mcp->mb[12]));
if (cur_xchg_cnt)
*cur_xchg_cnt = mcp->mb[3];
@@ -2041,6 +2044,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
*orig_iocb_cnt = mcp->mb[10];
if (vha->hw->flags.npiv_supported && max_npiv_vports)
*max_npiv_vports = mcp->mb[11];
+ if (IS_QLA81XX(vha->hw) && max_fcfs)
+ *max_fcfs = mcp->mb[12];
}
return (rval);
@@ -2313,6 +2318,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
{
int rval, rval2;
struct tsk_mgmt_cmd *tsk;
+ struct sts_entry_24xx *sts;
dma_addr_t tsk_dma;
scsi_qla_host_t *vha;
struct qla_hw_data *ha;
@@ -2352,20 +2358,37 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
sizeof(tsk->p.tsk.lun));
}
+ sts = &tsk->p.sts;
rval = qla2x00_issue_iocb(vha, tsk, tsk_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue %s Reset IOCB "
"(%x).\n", __func__, vha->host_no, name, rval));
- } else if (tsk->p.sts.entry_status != 0) {
+ } else if (sts->entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
"-- error status (%x).\n", __func__, vha->host_no,
- tsk->p.sts.entry_status));
+ sts->entry_status));
rval = QLA_FUNCTION_FAILED;
- } else if (tsk->p.sts.comp_status !=
+ } else if (sts->comp_status !=
__constant_cpu_to_le16(CS_COMPLETE)) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
"-- completion status (%x).\n", __func__,
- vha->host_no, le16_to_cpu(tsk->p.sts.comp_status)));
+ vha->host_no, le16_to_cpu(sts->comp_status)));
+ rval = QLA_FUNCTION_FAILED;
+ } else if (!(le16_to_cpu(sts->scsi_status) &
+ SS_RESPONSE_INFO_LEN_VALID)) {
+ DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+ "-- no response info (%x).\n", __func__, vha->host_no,
+ le16_to_cpu(sts->scsi_status)));
+ rval = QLA_FUNCTION_FAILED;
+ } else if (le32_to_cpu(sts->rsp_data_len) < 4) {
+ DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+ "-- not enough response info (%d).\n", __func__,
+ vha->host_no, le32_to_cpu(sts->rsp_data_len)));
+ rval = QLA_FUNCTION_FAILED;
+ } else if (sts->data[3]) {
+ DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+ "-- response (%x).\n", __func__,
+ vha->host_no, sts->data[3]));
rval = QLA_FUNCTION_FAILED;
}
@@ -2759,8 +2782,10 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
vp_idx, MSB(stat),
rptid_entry->port_id[2], rptid_entry->port_id[1],
rptid_entry->port_id[0]));
- if (vp_idx == 0)
- return;
+
+ vp = vha;
+ if (vp_idx == 0 && (MSB(stat) != 1))
+ goto reg_needed;
if (MSB(stat) == 1) {
DEBUG2(printk("scsi(%ld): Could not acquire ID for "
@@ -2783,8 +2808,11 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
* response queue. Handle it in dpc context.
*/
set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
- set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
+reg_needed:
+ set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
+ set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
}
}
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index e07b3617f019..a47d34308a3a 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -382,8 +382,6 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
vha->mgmt_svr_loop_id = 10 + vha->vp_idx;
vha->dpc_flags = 0L;
- set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
- set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
/*
* To fix the issue of processing a parent's RSCN for the vport before
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 2f90635e5aae..41669357b186 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
+#include <linux/kobject.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsicam.h>
@@ -2639,6 +2640,37 @@ qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE);
qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT);
qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE);
+int
+qla2x00_post_uevent_work(struct scsi_qla_host *vha, u32 code)
+{
+ struct qla_work_evt *e;
+
+ e = qla2x00_alloc_work(vha, QLA_EVT_UEVENT);
+ if (!e)
+ return QLA_FUNCTION_FAILED;
+
+ e->u.uevent.code = code;
+ return qla2x00_post_work(vha, e);
+}
+
+static void
+qla2x00_uevent_emit(struct scsi_qla_host *vha, u32 code)
+{
+ char event_string[40];
+ char *envp[] = { event_string, NULL };
+
+ switch (code) {
+ case QLA_UEVENT_CODE_FW_DUMP:
+ snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld",
+ vha->host_no);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ kobject_uevent_env(&vha->hw->pdev->dev.kobj, KOBJ_CHANGE, envp);
+}
+
void
qla2x00_do_work(struct scsi_qla_host *vha)
{
@@ -2676,6 +2708,9 @@ qla2x00_do_work(struct scsi_qla_host *vha)
qla2x00_async_logout_done(vha, e->u.logio.fcport,
e->u.logio.data);
break;
+ case QLA_EVT_UEVENT:
+ qla2x00_uevent_emit(vha, e->u.uevent.code);
+ break;
}
if (e->flags & QLA_EVT_FLAG_FREE)
kfree(e);
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index ac107a2c34a4..807e0dbc67fa 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -7,7 +7,7 @@
/*
* Driver version
*/
-#define QLA2XXX_VERSION "8.03.01-k6"
+#define QLA2XXX_VERSION "8.03.01-k7"
#define QLA_DRIVER_MAJOR_VER 8
#define QLA_DRIVER_MINOR_VER 3
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index abe3aa67ed00..38f4abbd5550 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -135,6 +135,45 @@ config USB_CYTHERM
To compile this driver as a module, choose M here: the
module will be called cytherm.
+config USB_PHIDGET
+ tristate "USB Phidgets drivers"
+ depends on USB
+ help
+ Say Y here to enable the various drivers for devices from
+ Phidgets inc.
+
+config USB_PHIDGETKIT
+ tristate "USB PhidgetInterfaceKit support"
+ depends on USB_PHIDGET
+ help
+ Say Y here if you want to connect a PhidgetInterfaceKit USB device
+ from Phidgets Inc.
+
+ To compile this driver as a module, choose M here: the
+ module will be called phidgetkit.
+
+config USB_PHIDGETMOTORCONTROL
+ tristate "USB PhidgetMotorControl support"
+ depends on USB_PHIDGET
+ help
+ Say Y here if you want to connect a PhidgetMotorControl USB device
+ from Phidgets Inc.
+
+ To compile this driver as a module, choose M here: the
+ module will be called phidgetmotorcontrol.
+
+config USB_PHIDGETSERVO
+ tristate "USB PhidgetServo support"
+ depends on USB_PHIDGET
+ help
+ Say Y here if you want to connect an 1 or 4 Motor PhidgetServo
+ servo controller version 2.0 or 3.0.
+
+ Phidgets Inc. has a web page at <http://www.phidgets.com/>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called phidgetservo.
+
config USB_IDMOUSE
tristate "Siemens ID USB Mouse Fingerprint sensor support"
depends on USB
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 0826aab8303f..1334f7bdd7be 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -18,6 +18,10 @@ obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LD) += ldusb.o
obj-$(CONFIG_USB_LED) += usbled.o
obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
+obj-$(CONFIG_USB_PHIDGET) += phidget.o
+obj-$(CONFIG_USB_PHIDGETKIT) += phidgetkit.o
+obj-$(CONFIG_USB_PHIDGETMOTORCONTROL) += phidgetmotorcontrol.o
+obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o
obj-$(CONFIG_USB_RIO500) += rio500.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o
diff --git a/drivers/usb/misc/phidget.c b/drivers/usb/misc/phidget.c
new file mode 100644
index 000000000000..735ed33f4f7f
--- /dev/null
+++ b/drivers/usb/misc/phidget.c
@@ -0,0 +1,43 @@
+/*
+ * USB Phidgets class
+ *
+ * Copyright (C) 2006 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+struct class *phidget_class;
+
+static int __init init_phidget(void)
+{
+ phidget_class = class_create(THIS_MODULE, "phidget");
+
+ if (IS_ERR(phidget_class))
+ return PTR_ERR(phidget_class);
+
+ return 0;
+}
+
+static void __exit cleanup_phidget(void)
+{
+ class_destroy(phidget_class);
+}
+
+EXPORT_SYMBOL_GPL(phidget_class);
+
+module_init(init_phidget);
+module_exit(cleanup_phidget);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_DESCRIPTION("Container module for phidget class");
+
diff --git a/drivers/usb/misc/phidget.h b/drivers/usb/misc/phidget.h
new file mode 100644
index 000000000000..c4011907d431
--- /dev/null
+++ b/drivers/usb/misc/phidget.h
@@ -0,0 +1,12 @@
+/*
+ * USB Phidgets class
+ *
+ * Copyright (C) 2006 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+extern struct class *phidget_class;
diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c
new file mode 100644
index 000000000000..9a92b57a564d
--- /dev/null
+++ b/drivers/usb/misc/phidgetkit.c
@@ -0,0 +1,740 @@
+/*
+ * USB PhidgetInterfaceKit driver 1.0
+ *
+ * Copyright (C) 2004, 2006 Sean Young <sean@mess.org>
+ * Copyright (C) 2005 Daniel Saakes <daniel@saakes.net>
+ * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is a driver for the USB PhidgetInterfaceKit.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "phidget.h"
+
+#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
+#define DRIVER_DESC "USB PhidgetInterfaceKit Driver"
+
+#define USB_VENDOR_ID_GLAB 0x06c2
+#define USB_DEVICE_ID_INTERFACEKIT004 0x0040
+#define USB_DEVICE_ID_INTERFACEKIT01616 0x0044
+#define USB_DEVICE_ID_INTERFACEKIT888 0x0045
+#define USB_DEVICE_ID_INTERFACEKIT047 0x0051
+#define USB_DEVICE_ID_INTERFACEKIT088 0x0053
+
+#define USB_VENDOR_ID_WISEGROUP 0x0925
+#define USB_DEVICE_ID_INTERFACEKIT884 0x8201
+
+#define MAX_INTERFACES 16
+
+#define URB_INT_SIZE 8
+
+struct driver_interfacekit {
+ int sensors;
+ int inputs;
+ int outputs;
+ int has_lcd;
+ int amnesiac;
+};
+
+#define ifkit(_sensors, _inputs, _outputs, _lcd, _amnesiac) \
+{ \
+ .sensors = _sensors, \
+ .inputs = _inputs, \
+ .outputs = _outputs, \
+ .has_lcd = _lcd, \
+ .amnesiac = _amnesiac \
+};
+
+static const struct driver_interfacekit ph_004 = ifkit(0, 0, 4, 0, 0);
+static const struct driver_interfacekit ph_888n = ifkit(8, 8, 8, 0, 1);
+static const struct driver_interfacekit ph_888o = ifkit(8, 8, 8, 0, 0);
+static const struct driver_interfacekit ph_047 = ifkit(0, 4, 7, 1, 0);
+static const struct driver_interfacekit ph_884 = ifkit(8, 8, 4, 0, 0);
+static const struct driver_interfacekit ph_088 = ifkit(0, 8, 8, 1, 0);
+static const struct driver_interfacekit ph_01616 = ifkit(0, 16, 16, 0, 0);
+
+static unsigned long device_no;
+
+struct interfacekit {
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct driver_interfacekit *ifkit;
+ struct device *dev;
+ unsigned long outputs;
+ int dev_no;
+ u8 inputs[MAX_INTERFACES];
+ u16 sensors[MAX_INTERFACES];
+ u8 lcd_files_on;
+
+ struct urb *irq;
+ unsigned char *data;
+ dma_addr_t data_dma;
+
+ struct delayed_work do_notify;
+ struct delayed_work do_resubmit;
+ unsigned long input_events;
+ unsigned long sensor_events;
+};
+
+static struct usb_device_id id_table[] = {
+ {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT004),
+ .driver_info = (kernel_ulong_t)&ph_004},
+ {USB_DEVICE_VER(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888, 0, 0x814),
+ .driver_info = (kernel_ulong_t)&ph_888o},
+ {USB_DEVICE_VER(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888, 0x0815, 0xffff),
+ .driver_info = (kernel_ulong_t)&ph_888n},
+ {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT047),
+ .driver_info = (kernel_ulong_t)&ph_047},
+ {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT088),
+ .driver_info = (kernel_ulong_t)&ph_088},
+ {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT01616),
+ .driver_info = (kernel_ulong_t)&ph_01616},
+ {USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_INTERFACEKIT884),
+ .driver_info = (kernel_ulong_t)&ph_884},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int set_outputs(struct interfacekit *kit)
+{
+ u8 *buffer;
+ int retval;
+
+ buffer = kzalloc(4, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&kit->udev->dev, "%s - out of memory\n", __func__);
+ return -ENOMEM;
+ }
+ buffer[0] = (u8)kit->outputs;
+ buffer[1] = (u8)(kit->outputs >> 8);
+
+ dev_dbg(&kit->udev->dev, "sending data: 0x%04x\n", (u16)kit->outputs);
+
+ retval = usb_control_msg(kit->udev,
+ usb_sndctrlpipe(kit->udev, 0),
+ 0x09, 0x21, 0x0200, 0x0000, buffer, 4, 2000);
+
+ if (retval != 4)
+ dev_err(&kit->udev->dev, "usb_control_msg returned %d\n",
+ retval);
+ kfree(buffer);
+
+ if (kit->ifkit->amnesiac)
+ schedule_delayed_work(&kit->do_resubmit, HZ / 2);
+
+ return retval < 0 ? retval : 0;
+}
+
+static int change_string(struct interfacekit *kit, const char *display, unsigned char row)
+{
+ unsigned char *buffer;
+ unsigned char *form_buffer;
+ int retval = -ENOMEM;
+ int i,j, len, buf_ptr;
+
+ buffer = kmalloc(8, GFP_KERNEL);
+ form_buffer = kmalloc(30, GFP_KERNEL);
+ if ((!buffer) || (!form_buffer)) {
+ dev_err(&kit->udev->dev, "%s - out of memory\n", __func__);
+ goto exit;
+ }
+
+ len = strlen(display);
+ if (len > 20)
+ len = 20;
+
+ dev_dbg(&kit->udev->dev, "Setting LCD line %d to %s\n", row, display);
+
+ form_buffer[0] = row * 0x40 + 0x80;
+ form_buffer[1] = 0x02;
+ buf_ptr = 2;
+ for (i = 0; i<len; i++)
+ form_buffer[buf_ptr++] = display[i];
+
+ for (i = 0; i < (20 - len); i++)
+ form_buffer[buf_ptr++] = 0x20;
+ form_buffer[buf_ptr++] = 0x01;
+ form_buffer[buf_ptr++] = row * 0x40 + 0x80 + strlen(display);
+
+ for (i = 0; i < buf_ptr; i += 7) {
+ if ((buf_ptr - i) > 7)
+ len = 7;
+ else
+ len = (buf_ptr - i);
+ for (j = 0; j < len; j++)
+ buffer[j] = form_buffer[i + j];
+ buffer[7] = len;
+
+ retval = usb_control_msg(kit->udev,
+ usb_sndctrlpipe(kit->udev, 0),
+ 0x09, 0x21, 0x0200, 0x0000, buffer, 8, 2000);
+ if (retval < 0)
+ goto exit;
+ }
+
+ retval = 0;
+exit:
+ kfree(buffer);
+ kfree(form_buffer);
+
+ return retval;
+}
+
+#define set_lcd_line(number) \
+static ssize_t lcd_line_##number(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
+ change_string(kit, buf, number - 1); \
+ return count; \
+}
+
+#define lcd_line_attr(number) \
+ __ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number)
+
+set_lcd_line(1);
+set_lcd_line(2);
+
+static ssize_t set_backlight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct interfacekit *kit = dev_get_drvdata(dev);
+ int enabled;
+ unsigned char *buffer;
+ int retval = -ENOMEM;
+
+ buffer = kzalloc(8, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&kit->udev->dev, "%s - out of memory\n", __func__);
+ goto exit;
+ }
+
+ if (sscanf(buf, "%d", &enabled) < 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+ if (enabled)
+ buffer[0] = 0x01;
+ buffer[7] = 0x11;
+
+ dev_dbg(&kit->udev->dev, "Setting backlight to %s\n", enabled ? "on" : "off");
+
+ retval = usb_control_msg(kit->udev,
+ usb_sndctrlpipe(kit->udev, 0),
+ 0x09, 0x21, 0x0200, 0x0000, buffer, 8, 2000);
+ if (retval < 0)
+ goto exit;
+
+ retval = count;
+exit:
+ kfree(buffer);
+ return retval;
+}
+
+static struct device_attribute dev_lcd_line_attrs[] = {
+ lcd_line_attr(1),
+ lcd_line_attr(2),
+ __ATTR(backlight, S_IWUGO, NULL, set_backlight)
+};
+
+static void remove_lcd_files(struct interfacekit *kit)
+{
+ int i;
+
+ if (kit->lcd_files_on) {
+ dev_dbg(&kit->udev->dev, "Removing lcd files\n");
+
+ for (i=0; i<ARRAY_SIZE(dev_lcd_line_attrs); i++)
+ device_remove_file(kit->dev, &dev_lcd_line_attrs[i]);
+ }
+}
+
+static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct interfacekit *kit = dev_get_drvdata(dev);
+ int enable;
+ int i, rc;
+
+ if (kit->ifkit->has_lcd == 0)
+ return -ENODEV;
+
+ if (sscanf(buf, "%d", &enable) < 1)
+ return -EINVAL;
+
+ if (enable) {
+ if (!kit->lcd_files_on) {
+ dev_dbg(&kit->udev->dev, "Adding lcd files\n");
+ for (i=0; i<ARRAY_SIZE(dev_lcd_line_attrs); i++) {
+ rc = device_create_file(kit->dev,
+ &dev_lcd_line_attrs[i]);
+ if (rc)
+ goto out;
+ }
+ kit->lcd_files_on = 1;
+ }
+ } else {
+ if (kit->lcd_files_on) {
+ remove_lcd_files(kit);
+ kit->lcd_files_on = 0;
+ }
+ }
+
+ return count;
+out:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_lcd_line_attrs[i]);
+
+ return rc;
+}
+
+static DEVICE_ATTR(lcd, S_IWUGO, NULL, enable_lcd_files);
+
+static void interfacekit_irq(struct urb *urb)
+{
+ struct interfacekit *kit = urb->context;
+ unsigned char *buffer = kit->data;
+ int i, level, sensor;
+ int retval;
+ int status = urb->status;
+
+ switch (status) {
+ case 0: /* success */
+ break;
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ /* -EPIPE: should clear the halt */
+ default: /* error */
+ goto resubmit;
+ }
+
+ /* digital inputs */
+ if (kit->ifkit->inputs == 16) {
+ for (i=0; i < 8; i++) {
+ level = (buffer[0] >> i) & 1;
+ if (kit->inputs[i] != level) {
+ kit->inputs[i] = level;
+ set_bit(i, &kit->input_events);
+ }
+ level = (buffer[1] >> i) & 1;
+ if (kit->inputs[8 + i] != level) {
+ kit->inputs[8 + i] = level;
+ set_bit(8 + i, &kit->input_events);
+ }
+ }
+ }
+ else if (kit->ifkit->inputs == 8) {
+ for (i=0; i < 8; i++) {
+ level = (buffer[1] >> i) & 1;
+ if (kit->inputs[i] != level) {
+ kit->inputs[i] = level;
+ set_bit(i, &kit->input_events);
+ }
+ }
+ }
+
+ /* analog inputs */
+ if (kit->ifkit->sensors) {
+ sensor = (buffer[0] & 1) ? 4 : 0;
+
+ level = buffer[2] + (buffer[3] & 0x0f) * 256;
+ if (level != kit->sensors[sensor]) {
+ kit->sensors[sensor] = level;
+ set_bit(sensor, &kit->sensor_events);
+ }
+ sensor++;
+ level = buffer[4] + (buffer[3] & 0xf0) * 16;
+ if (level != kit->sensors[sensor]) {
+ kit->sensors[sensor] = level;
+ set_bit(sensor, &kit->sensor_events);
+ }
+ sensor++;
+ level = buffer[5] + (buffer[6] & 0x0f) * 256;
+ if (level != kit->sensors[sensor]) {
+ kit->sensors[sensor] = level;
+ set_bit(sensor, &kit->sensor_events);
+ }
+ sensor++;
+ level = buffer[7] + (buffer[6] & 0xf0) * 16;
+ if (level != kit->sensors[sensor]) {
+ kit->sensors[sensor] = level;
+ set_bit(sensor, &kit->sensor_events);
+ }
+ }
+
+ if (kit->input_events || kit->sensor_events)
+ schedule_delayed_work(&kit->do_notify, 0);
+
+resubmit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err("can't resubmit intr, %s-%s/interfacekit0, retval %d",
+ kit->udev->bus->bus_name,
+ kit->udev->devpath, retval);
+}
+
+static void do_notify(struct work_struct *work)
+{
+ struct interfacekit *kit =
+ container_of(work, struct interfacekit, do_notify.work);
+ int i;
+ char sysfs_file[8];
+
+ for (i=0; i<kit->ifkit->inputs; i++) {
+ if (test_and_clear_bit(i, &kit->input_events)) {
+ sprintf(sysfs_file, "input%d", i + 1);
+ sysfs_notify(&kit->dev->kobj, NULL, sysfs_file);
+ }
+ }
+
+ for (i=0; i<kit->ifkit->sensors; i++) {
+ if (test_and_clear_bit(i, &kit->sensor_events)) {
+ sprintf(sysfs_file, "sensor%d", i + 1);
+ sysfs_notify(&kit->dev->kobj, NULL, sysfs_file);
+ }
+ }
+}
+
+static void do_resubmit(struct work_struct *work)
+{
+ struct interfacekit *kit =
+ container_of(work, struct interfacekit, do_resubmit.work);
+ set_outputs(kit);
+}
+
+#define show_set_output(value) \
+static ssize_t set_output##value(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
+ int enable; \
+ int retval; \
+ \
+ if (sscanf(buf, "%d", &enable) < 1) \
+ return -EINVAL; \
+ \
+ if (enable) \
+ set_bit(value - 1, &kit->outputs); \
+ else \
+ clear_bit(value - 1, &kit->outputs); \
+ \
+ retval = set_outputs(kit); \
+ \
+ return retval ? retval : count; \
+} \
+ \
+static ssize_t show_output##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", !!test_bit(value - 1, &kit->outputs));\
+}
+
+#define output_attr(value) \
+ __ATTR(output##value, S_IWUGO | S_IRUGO, \
+ show_output##value, set_output##value)
+
+show_set_output(1);
+show_set_output(2);
+show_set_output(3);
+show_set_output(4);
+show_set_output(5);
+show_set_output(6);
+show_set_output(7);
+show_set_output(8);
+show_set_output(9);
+show_set_output(10);
+show_set_output(11);
+show_set_output(12);
+show_set_output(13);
+show_set_output(14);
+show_set_output(15);
+show_set_output(16);
+
+static struct device_attribute dev_output_attrs[] = {
+ output_attr(1), output_attr(2), output_attr(3), output_attr(4),
+ output_attr(5), output_attr(6), output_attr(7), output_attr(8),
+ output_attr(9), output_attr(10), output_attr(11), output_attr(12),
+ output_attr(13), output_attr(14), output_attr(15), output_attr(16)
+};
+
+#define show_input(value) \
+static ssize_t show_input##value(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", (int)kit->inputs[value - 1]); \
+}
+
+#define input_attr(value) \
+ __ATTR(input##value, S_IRUGO, show_input##value, NULL)
+
+show_input(1);
+show_input(2);
+show_input(3);
+show_input(4);
+show_input(5);
+show_input(6);
+show_input(7);
+show_input(8);
+show_input(9);
+show_input(10);
+show_input(11);
+show_input(12);
+show_input(13);
+show_input(14);
+show_input(15);
+show_input(16);
+
+static struct device_attribute dev_input_attrs[] = {
+ input_attr(1), input_attr(2), input_attr(3), input_attr(4),
+ input_attr(5), input_attr(6), input_attr(7), input_attr(8),
+ input_attr(9), input_attr(10), input_attr(11), input_attr(12),
+ input_attr(13), input_attr(14), input_attr(15), input_attr(16)
+};
+
+#define show_sensor(value) \
+static ssize_t show_sensor##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", (int)kit->sensors[value - 1]); \
+}
+
+#define sensor_attr(value) \
+ __ATTR(sensor##value, S_IRUGO, show_sensor##value, NULL)
+
+show_sensor(1);
+show_sensor(2);
+show_sensor(3);
+show_sensor(4);
+show_sensor(5);
+show_sensor(6);
+show_sensor(7);
+show_sensor(8);
+
+static struct device_attribute dev_sensor_attrs[] = {
+ sensor_attr(1), sensor_attr(2), sensor_attr(3), sensor_attr(4),
+ sensor_attr(5), sensor_attr(6), sensor_attr(7), sensor_attr(8)
+};
+
+static int interfacekit_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct interfacekit *kit;
+ struct driver_interfacekit *ifkit;
+ int pipe, maxp, rc = -ENOMEM;
+ int bit, value, i;
+
+ ifkit = (struct driver_interfacekit *)id->driver_info;
+ if (!ifkit)
+ return -ENODEV;
+
+ interface = intf->cur_altsetting;
+ if (interface->desc.bNumEndpoints != 1)
+ return -ENODEV;
+
+ endpoint = &interface->endpoint[0].desc;
+ if (!usb_endpoint_dir_in(endpoint))
+ return -ENODEV;
+ /*
+ * bmAttributes
+ */
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+ kit = kzalloc(sizeof(*kit), GFP_KERNEL);
+ if (!kit)
+ goto out;
+
+ kit->dev_no = -1;
+ kit->ifkit = ifkit;
+ kit->data = usb_buffer_alloc(dev, URB_INT_SIZE, GFP_ATOMIC, &kit->data_dma);
+ if (!kit->data)
+ goto out;
+
+ kit->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!kit->irq)
+ goto out;
+
+ kit->udev = usb_get_dev(dev);
+ kit->intf = intf;
+ INIT_DELAYED_WORK(&kit->do_notify, do_notify);
+ INIT_DELAYED_WORK(&kit->do_resubmit, do_resubmit);
+ usb_fill_int_urb(kit->irq, kit->udev, pipe, kit->data,
+ maxp > URB_INT_SIZE ? URB_INT_SIZE : maxp,
+ interfacekit_irq, kit, endpoint->bInterval);
+ kit->irq->transfer_dma = kit->data_dma;
+ kit->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, kit);
+
+ do {
+ bit = find_first_zero_bit(&device_no, sizeof(device_no));
+ value = test_and_set_bit(bit, &device_no);
+ } while(value);
+ kit->dev_no = bit;
+
+ kit->dev = device_create(phidget_class, &kit->udev->dev, MKDEV(0, 0),
+ kit, "interfacekit%d", kit->dev_no);
+ if (IS_ERR(kit->dev)) {
+ rc = PTR_ERR(kit->dev);
+ kit->dev = NULL;
+ goto out;
+ }
+
+ if (usb_submit_urb(kit->irq, GFP_KERNEL)) {
+ rc = -EIO;
+ goto out;
+ }
+
+ for (i=0; i<ifkit->outputs; i++ ) {
+ rc = device_create_file(kit->dev, &dev_output_attrs[i]);
+ if (rc)
+ goto out2;
+ }
+
+ for (i=0; i<ifkit->inputs; i++ ) {
+ rc = device_create_file(kit->dev, &dev_input_attrs[i]);
+ if (rc)
+ goto out3;
+ }
+
+ for (i=0; i<ifkit->sensors; i++ ) {
+ rc = device_create_file(kit->dev, &dev_sensor_attrs[i]);
+ if (rc)
+ goto out4;
+ }
+
+ if (ifkit->has_lcd) {
+ rc = device_create_file(kit->dev, &dev_attr_lcd);
+ if (rc)
+ goto out4;
+
+ }
+
+ dev_info(&intf->dev, "USB PhidgetInterfaceKit %d/%d/%d attached\n",
+ ifkit->sensors, ifkit->inputs, ifkit->outputs);
+
+ return 0;
+
+out4:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_sensor_attrs[i]);
+
+ i = ifkit->inputs;
+out3:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_input_attrs[i]);
+
+ i = ifkit->outputs;
+out2:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_output_attrs[i]);
+out:
+ if (kit) {
+ usb_free_urb(kit->irq);
+ if (kit->data)
+ usb_buffer_free(dev, URB_INT_SIZE, kit->data, kit->data_dma);
+ if (kit->dev)
+ device_unregister(kit->dev);
+ if (kit->dev_no >= 0)
+ clear_bit(kit->dev_no, &device_no);
+
+ kfree(kit);
+ }
+
+ return rc;
+}
+
+static void interfacekit_disconnect(struct usb_interface *interface)
+{
+ struct interfacekit *kit;
+ int i;
+
+ kit = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+ if (!kit)
+ return;
+
+ usb_kill_urb(kit->irq);
+ usb_free_urb(kit->irq);
+ usb_buffer_free(kit->udev, URB_INT_SIZE, kit->data, kit->data_dma);
+
+ cancel_delayed_work(&kit->do_notify);
+ cancel_delayed_work(&kit->do_resubmit);
+
+ for (i=0; i<kit->ifkit->outputs; i++)
+ device_remove_file(kit->dev, &dev_output_attrs[i]);
+
+ for (i=0; i<kit->ifkit->inputs; i++)
+ device_remove_file(kit->dev, &dev_input_attrs[i]);
+
+ for (i=0; i<kit->ifkit->sensors; i++)
+ device_remove_file(kit->dev, &dev_sensor_attrs[i]);
+
+ if (kit->ifkit->has_lcd) {
+ device_remove_file(kit->dev, &dev_attr_lcd);
+ remove_lcd_files(kit);
+ }
+
+ device_unregister(kit->dev);
+
+ dev_info(&interface->dev, "USB PhidgetInterfaceKit %d/%d/%d detached\n",
+ kit->ifkit->sensors, kit->ifkit->inputs, kit->ifkit->outputs);
+
+ usb_put_dev(kit->udev);
+ clear_bit(kit->dev_no, &device_no);
+
+ kfree(kit);
+}
+
+static struct usb_driver interfacekit_driver = {
+ .name = "phidgetkit",
+ .probe = interfacekit_probe,
+ .disconnect = interfacekit_disconnect,
+ .id_table = id_table
+};
+
+static int __init interfacekit_init(void)
+{
+ int retval = 0;
+
+ retval = usb_register(&interfacekit_driver);
+ if (retval)
+ err("usb_register failed. Error number %d", retval);
+
+ return retval;
+}
+
+static void __exit interfacekit_exit(void)
+{
+ usb_deregister(&interfacekit_driver);
+}
+
+module_init(interfacekit_init);
+module_exit(interfacekit_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/phidgetmotorcontrol.c b/drivers/usb/misc/phidgetmotorcontrol.c
new file mode 100644
index 000000000000..38088b44349e
--- /dev/null
+++ b/drivers/usb/misc/phidgetmotorcontrol.c
@@ -0,0 +1,465 @@
+/*
+ * USB Phidget MotorControl driver
+ *
+ * Copyright (C) 2006 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "phidget.h"
+
+#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
+#define DRIVER_DESC "USB PhidgetMotorControl Driver"
+
+#define USB_VENDOR_ID_GLAB 0x06c2
+#define USB_DEVICE_ID_MOTORCONTROL 0x0058
+
+#define URB_INT_SIZE 8
+
+static unsigned long device_no;
+
+struct motorcontrol {
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct device *dev;
+ int dev_no;
+ u8 inputs[4];
+ s8 desired_speed[2];
+ s8 speed[2];
+ s16 _current[2];
+ s8 acceleration[2];
+ struct urb *irq;
+ unsigned char *data;
+ dma_addr_t data_dma;
+
+ struct delayed_work do_notify;
+ unsigned long input_events;
+ unsigned long speed_events;
+ unsigned long exceed_events;
+};
+
+static struct usb_device_id id_table[] = {
+ { USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_MOTORCONTROL) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int set_motor(struct motorcontrol *mc, int motor)
+{
+ u8 *buffer;
+ int speed, speed2, acceleration;
+ int retval;
+
+ buffer = kzalloc(8, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&mc->intf->dev, "%s - out of memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ acceleration = mc->acceleration[motor] * 10;
+ /* -127 <= speed <= 127 */
+ speed = (mc->desired_speed[motor] * 127) / 100;
+ /* -0x7300 <= speed2 <= 0x7300 */
+ speed2 = (mc->desired_speed[motor] * 230 * 128) / 100;
+
+ buffer[0] = motor;
+ buffer[1] = speed;
+ buffer[2] = acceleration >> 8;
+ buffer[3] = acceleration;
+ buffer[4] = speed2 >> 8;
+ buffer[5] = speed2;
+
+ retval = usb_control_msg(mc->udev,
+ usb_sndctrlpipe(mc->udev, 0),
+ 0x09, 0x21, 0x0200, 0x0000, buffer, 8, 2000);
+
+ if (retval != 8)
+ dev_err(&mc->intf->dev, "usb_control_msg returned %d\n",
+ retval);
+ kfree(buffer);
+
+ return retval < 0 ? retval : 0;
+}
+
+static void motorcontrol_irq(struct urb *urb)
+{
+ struct motorcontrol *mc = urb->context;
+ unsigned char *buffer = mc->data;
+ int i, level;
+ int retval;
+ int status = urb->status;;
+
+ switch (status) {
+ case 0: /* success */
+ break;
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ /* -EPIPE: should clear the halt */
+ default: /* error */
+ goto resubmit;
+ }
+
+ /* digital inputs */
+ for (i=0; i<4; i++) {
+ level = (buffer[0] >> i) & 1;
+ if (mc->inputs[i] != level) {
+ mc->inputs[i] = level;
+ set_bit(i, &mc->input_events);
+ }
+ }
+
+ /* motor speed */
+ if (buffer[2] == 0) {
+ for (i=0; i<2; i++) {
+ level = ((s8)buffer[4+i]) * 100 / 127;
+ if (mc->speed[i] != level) {
+ mc->speed[i] = level;
+ set_bit(i, &mc->speed_events);
+ }
+ }
+ } else {
+ int index = buffer[3] & 1;
+
+ level = ((s8)buffer[4] << 8) | buffer[5];
+ level = level * 100 / 29440;
+ if (mc->speed[index] != level) {
+ mc->speed[index] = level;
+ set_bit(index, &mc->speed_events);
+ }
+
+ level = ((s8)buffer[6] << 8) | buffer[7];
+ mc->_current[index] = level * 100 / 1572;
+ }
+
+ if (buffer[1] & 1)
+ set_bit(0, &mc->exceed_events);
+
+ if (buffer[1] & 2)
+ set_bit(1, &mc->exceed_events);
+
+ if (mc->input_events || mc->exceed_events || mc->speed_events)
+ schedule_delayed_work(&mc->do_notify, 0);
+
+resubmit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&mc->intf->dev,
+ "can't resubmit intr, %s-%s/motorcontrol0, retval %d\n",
+ mc->udev->bus->bus_name,
+ mc->udev->devpath, retval);
+}
+
+static void do_notify(struct work_struct *work)
+{
+ struct motorcontrol *mc =
+ container_of(work, struct motorcontrol, do_notify.work);
+ int i;
+ char sysfs_file[8];
+
+ for (i=0; i<4; i++) {
+ if (test_and_clear_bit(i, &mc->input_events)) {
+ sprintf(sysfs_file, "input%d", i);
+ sysfs_notify(&mc->dev->kobj, NULL, sysfs_file);
+ }
+ }
+
+ for (i=0; i<2; i++) {
+ if (test_and_clear_bit(i, &mc->speed_events)) {
+ sprintf(sysfs_file, "speed%d", i);
+ sysfs_notify(&mc->dev->kobj, NULL, sysfs_file);
+ }
+ }
+
+ for (i=0; i<2; i++) {
+ if (test_and_clear_bit(i, &mc->exceed_events))
+ dev_warn(&mc->intf->dev,
+ "motor #%d exceeds 1.5 Amp current limit\n", i);
+ }
+}
+
+#define show_set_speed(value) \
+static ssize_t set_speed##value(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ int speed; \
+ int retval; \
+ \
+ if (sscanf(buf, "%d", &speed) < 1) \
+ return -EINVAL; \
+ \
+ if (speed < -100 || speed > 100) \
+ return -EINVAL; \
+ \
+ mc->desired_speed[value] = speed; \
+ \
+ retval = set_motor(mc, value); \
+ \
+ return retval ? retval : count; \
+} \
+ \
+static ssize_t show_speed##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", mc->speed[value]); \
+}
+
+#define speed_attr(value) \
+ __ATTR(speed##value, S_IWUGO | S_IRUGO, \
+ show_speed##value, set_speed##value)
+
+show_set_speed(0);
+show_set_speed(1);
+
+#define show_set_acceleration(value) \
+static ssize_t set_acceleration##value(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ int acceleration; \
+ int retval; \
+ \
+ if (sscanf(buf, "%d", &acceleration) < 1) \
+ return -EINVAL; \
+ \
+ if (acceleration < 0 || acceleration > 100) \
+ return -EINVAL; \
+ \
+ mc->acceleration[value] = acceleration; \
+ \
+ retval = set_motor(mc, value); \
+ \
+ return retval ? retval : count; \
+} \
+ \
+static ssize_t show_acceleration##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", mc->acceleration[value]); \
+}
+
+#define acceleration_attr(value) \
+ __ATTR(acceleration##value, S_IWUGO | S_IRUGO, \
+ show_acceleration##value, set_acceleration##value)
+
+show_set_acceleration(0);
+show_set_acceleration(1);
+
+#define show_current(value) \
+static ssize_t show_current##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%dmA\n", (int)mc->_current[value]); \
+}
+
+#define current_attr(value) \
+ __ATTR(current##value, S_IRUGO, show_current##value, NULL)
+
+show_current(0);
+show_current(1);
+
+#define show_input(value) \
+static ssize_t show_input##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", (int)mc->inputs[value]); \
+}
+
+#define input_attr(value) \
+ __ATTR(input##value, S_IRUGO, show_input##value, NULL)
+
+show_input(0);
+show_input(1);
+show_input(2);
+show_input(3);
+
+static struct device_attribute dev_attrs[] = {
+ input_attr(0),
+ input_attr(1),
+ input_attr(2),
+ input_attr(3),
+ speed_attr(0),
+ speed_attr(1),
+ acceleration_attr(0),
+ acceleration_attr(1),
+ current_attr(0),
+ current_attr(1)
+};
+
+static int motorcontrol_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct motorcontrol *mc;
+ int pipe, maxp, rc = -ENOMEM;
+ int bit, value, i;
+
+ interface = intf->cur_altsetting;
+ if (interface->desc.bNumEndpoints != 1)
+ return -ENODEV;
+
+ endpoint = &interface->endpoint[0].desc;
+ if (!usb_endpoint_dir_in(endpoint))
+ return -ENODEV;
+
+ /*
+ * bmAttributes
+ */
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+ mc = kzalloc(sizeof(*mc), GFP_KERNEL);
+ if (!mc)
+ goto out;
+
+ mc->dev_no = -1;
+ mc->data = usb_buffer_alloc(dev, URB_INT_SIZE, GFP_ATOMIC, &mc->data_dma);
+ if (!mc->data)
+ goto out;
+
+ mc->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!mc->irq)
+ goto out;
+
+ mc->udev = usb_get_dev(dev);
+ mc->intf = intf;
+ mc->acceleration[0] = mc->acceleration[1] = 10;
+ INIT_DELAYED_WORK(&mc->do_notify, do_notify);
+ usb_fill_int_urb(mc->irq, mc->udev, pipe, mc->data,
+ maxp > URB_INT_SIZE ? URB_INT_SIZE : maxp,
+ motorcontrol_irq, mc, endpoint->bInterval);
+ mc->irq->transfer_dma = mc->data_dma;
+ mc->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, mc);
+
+ do {
+ bit = find_first_zero_bit(&device_no, sizeof(device_no));
+ value = test_and_set_bit(bit, &device_no);
+ } while(value);
+ mc->dev_no = bit;
+
+ mc->dev = device_create(phidget_class, &mc->udev->dev, MKDEV(0, 0), mc,
+ "motorcontrol%d", mc->dev_no);
+ if (IS_ERR(mc->dev)) {
+ rc = PTR_ERR(mc->dev);
+ mc->dev = NULL;
+ goto out;
+ }
+
+ if (usb_submit_urb(mc->irq, GFP_KERNEL)) {
+ rc = -EIO;
+ goto out;
+ }
+
+ for (i=0; i<ARRAY_SIZE(dev_attrs); i++) {
+ rc = device_create_file(mc->dev, &dev_attrs[i]);
+ if (rc)
+ goto out2;
+ }
+
+ dev_info(&intf->dev, "USB PhidgetMotorControl attached\n");
+
+ return 0;
+out2:
+ while (i-- > 0)
+ device_remove_file(mc->dev, &dev_attrs[i]);
+out:
+ if (mc) {
+ usb_free_urb(mc->irq);
+ if (mc->data)
+ usb_buffer_free(dev, URB_INT_SIZE, mc->data, mc->data_dma);
+ if (mc->dev)
+ device_unregister(mc->dev);
+ if (mc->dev_no >= 0)
+ clear_bit(mc->dev_no, &device_no);
+
+ kfree(mc);
+ }
+
+ return rc;
+}
+
+static void motorcontrol_disconnect(struct usb_interface *interface)
+{
+ struct motorcontrol *mc;
+ int i;
+
+ mc = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+ if (!mc)
+ return;
+
+ usb_kill_urb(mc->irq);
+ usb_free_urb(mc->irq);
+ usb_buffer_free(mc->udev, URB_INT_SIZE, mc->data, mc->data_dma);
+
+ cancel_delayed_work(&mc->do_notify);
+
+ for (i=0; i<ARRAY_SIZE(dev_attrs); i++)
+ device_remove_file(mc->dev, &dev_attrs[i]);
+
+ device_unregister(mc->dev);
+
+ usb_put_dev(mc->udev);
+ clear_bit(mc->dev_no, &device_no);
+ kfree(mc);
+
+ dev_info(&interface->dev, "USB PhidgetMotorControl detached\n");
+}
+
+static struct usb_driver motorcontrol_driver = {
+ .name = "phidgetmotorcontrol",
+ .probe = motorcontrol_probe,
+ .disconnect = motorcontrol_disconnect,
+ .id_table = id_table
+};
+
+static int __init motorcontrol_init(void)
+{
+ int retval = 0;
+
+ retval = usb_register(&motorcontrol_driver);
+ if (retval)
+ err("usb_register failed. Error number %d", retval);
+
+ return retval;
+}
+
+static void __exit motorcontrol_exit(void)
+{
+ usb_deregister(&motorcontrol_driver);
+}
+
+module_init(motorcontrol_init);
+module_exit(motorcontrol_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c
new file mode 100644
index 000000000000..42f438292299
--- /dev/null
+++ b/drivers/usb/misc/phidgetservo.c
@@ -0,0 +1,375 @@
+/*
+ * USB PhidgetServo driver 1.0
+ *
+ * Copyright (C) 2004, 2006 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is a driver for the USB PhidgetServo version 2.0 and 3.0 servo
+ * controllers available at: http://www.phidgets.com/
+ *
+ * Note that the driver takes input as: degrees.minutes
+ *
+ * CAUTION: Generally you should use 0 < degrees < 180 as anything else
+ * is probably beyond the range of your servo and may damage it.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "phidget.h"
+
+#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
+#define DRIVER_DESC "USB PhidgetServo Driver"
+
+#define VENDOR_ID_GLAB 0x06c2
+#define DEVICE_ID_GLAB_PHIDGETSERVO_QUAD 0x0038
+#define DEVICE_ID_GLAB_PHIDGETSERVO_UNI 0x0039
+
+#define VENDOR_ID_WISEGROUP 0x0925
+#define VENDOR_ID_WISEGROUP_PHIDGETSERVO_QUAD 0x8101
+#define VENDOR_ID_WISEGROUP_PHIDGETSERVO_UNI 0x8104
+
+#define SERVO_VERSION_30 0x01
+#define SERVO_COUNT_QUAD 0x02
+
+static struct usb_device_id id_table[] = {
+ {
+ USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_GLAB_PHIDGETSERVO_QUAD),
+ .driver_info = SERVO_VERSION_30 | SERVO_COUNT_QUAD
+ },
+ {
+ USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_GLAB_PHIDGETSERVO_UNI),
+ .driver_info = SERVO_VERSION_30
+ },
+ {
+ USB_DEVICE(VENDOR_ID_WISEGROUP,
+ VENDOR_ID_WISEGROUP_PHIDGETSERVO_QUAD),
+ .driver_info = SERVO_COUNT_QUAD
+ },
+ {
+ USB_DEVICE(VENDOR_ID_WISEGROUP,
+ VENDOR_ID_WISEGROUP_PHIDGETSERVO_UNI),
+ .driver_info = 0
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int unsigned long device_no;
+
+struct phidget_servo {
+ struct usb_device *udev;
+ struct device *dev;
+ int dev_no;
+ ulong type;
+ int pulse[4];
+ int degrees[4];
+ int minutes[4];
+};
+
+static int
+change_position_v30(struct phidget_servo *servo, int servo_no, int degrees,
+ int minutes)
+{
+ int retval;
+ unsigned char *buffer;
+
+ if (degrees < -23 || degrees > 362)
+ return -EINVAL;
+
+ buffer = kmalloc(6, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&servo->udev->dev, "%s - out of memory\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /*
+ * pulse = 0 - 4095
+ * angle = 0 - 180 degrees
+ *
+ * pulse = angle * 10.6 + 243.8
+ */
+ servo->pulse[servo_no] = ((degrees*60 + minutes)*106 + 2438*60)/600;
+ servo->degrees[servo_no]= degrees;
+ servo->minutes[servo_no]= minutes;
+
+ /*
+ * The PhidgetServo v3.0 is controlled by sending 6 bytes,
+ * 4 * 12 bits for each servo.
+ *
+ * low = lower 8 bits pulse
+ * high = higher 4 bits pulse
+ *
+ * offset bits
+ * +---+-----------------+
+ * | 0 | low 0 |
+ * +---+--------+--------+
+ * | 1 | high 1 | high 0 |
+ * +---+--------+--------+
+ * | 2 | low 1 |
+ * +---+-----------------+
+ * | 3 | low 2 |
+ * +---+--------+--------+
+ * | 4 | high 3 | high 2 |
+ * +---+--------+--------+
+ * | 5 | low 3 |
+ * +---+-----------------+
+ */
+
+ buffer[0] = servo->pulse[0] & 0xff;
+ buffer[1] = (servo->pulse[0] >> 8 & 0x0f)
+ | (servo->pulse[1] >> 4 & 0xf0);
+ buffer[2] = servo->pulse[1] & 0xff;
+ buffer[3] = servo->pulse[2] & 0xff;
+ buffer[4] = (servo->pulse[2] >> 8 & 0x0f)
+ | (servo->pulse[3] >> 4 & 0xf0);
+ buffer[5] = servo->pulse[3] & 0xff;
+
+ dev_dbg(&servo->udev->dev,
+ "data: %02x %02x %02x %02x %02x %02x\n",
+ buffer[0], buffer[1], buffer[2],
+ buffer[3], buffer[4], buffer[5]);
+
+ retval = usb_control_msg(servo->udev,
+ usb_sndctrlpipe(servo->udev, 0),
+ 0x09, 0x21, 0x0200, 0x0000, buffer, 6, 2000);
+
+ kfree(buffer);
+
+ return retval;
+}
+
+static int
+change_position_v20(struct phidget_servo *servo, int servo_no, int degrees,
+ int minutes)
+{
+ int retval;
+ unsigned char *buffer;
+
+ if (degrees < -23 || degrees > 278)
+ return -EINVAL;
+
+ buffer = kmalloc(2, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&servo->udev->dev, "%s - out of memory\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /*
+ * angle = 0 - 180 degrees
+ * pulse = angle + 23
+ */
+ servo->pulse[servo_no]= degrees + 23;
+ servo->degrees[servo_no]= degrees;
+ servo->minutes[servo_no]= 0;
+
+ /*
+ * The PhidgetServo v2.0 is controlled by sending two bytes. The
+ * first byte is the servo number xor'ed with 2:
+ *
+ * servo 0 = 2
+ * servo 1 = 3
+ * servo 2 = 0
+ * servo 3 = 1
+ *
+ * The second byte is the position.
+ */
+
+ buffer[0] = servo_no ^ 2;
+ buffer[1] = servo->pulse[servo_no];
+
+ dev_dbg(&servo->udev->dev, "data: %02x %02x\n", buffer[0], buffer[1]);
+
+ retval = usb_control_msg(servo->udev,
+ usb_sndctrlpipe(servo->udev, 0),
+ 0x09, 0x21, 0x0200, 0x0000, buffer, 2, 2000);
+
+ kfree(buffer);
+
+ return retval;
+}
+
+#define show_set(value) \
+static ssize_t set_servo##value (struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ int degrees, minutes, retval; \
+ struct phidget_servo *servo = dev_get_drvdata(dev); \
+ \
+ minutes = 0; \
+ /* must at least convert degrees */ \
+ if (sscanf(buf, "%d.%d", &degrees, &minutes) < 1) { \
+ return -EINVAL; \
+ } \
+ \
+ if (minutes < 0 || minutes > 59) \
+ return -EINVAL; \
+ \
+ if (servo->type & SERVO_VERSION_30) \
+ retval = change_position_v30(servo, value, degrees, \
+ minutes); \
+ else \
+ retval = change_position_v20(servo, value, degrees, \
+ minutes); \
+ \
+ return retval < 0 ? retval : count; \
+} \
+ \
+static ssize_t show_servo##value (struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct phidget_servo *servo = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d.%02d\n", servo->degrees[value], \
+ servo->minutes[value]); \
+}
+
+#define servo_attr(value) \
+ __ATTR(servo##value, S_IWUGO | S_IRUGO, \
+ show_servo##value, set_servo##value)
+show_set(0);
+show_set(1);
+show_set(2);
+show_set(3);
+
+static struct device_attribute dev_attrs[] = {
+ servo_attr(0), servo_attr(1), servo_attr(2), servo_attr(3)
+};
+
+static int
+servo_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct phidget_servo *dev;
+ int bit, value, rc;
+ int servo_count, i;
+
+ dev = kzalloc(sizeof (struct phidget_servo), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&interface->dev, "%s - out of memory\n", __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ dev->udev = usb_get_dev(udev);
+ dev->type = id->driver_info;
+ dev->dev_no = -1;
+ usb_set_intfdata(interface, dev);
+
+ do {
+ bit = find_first_zero_bit(&device_no, sizeof(device_no));
+ value = test_and_set_bit(bit, &device_no);
+ } while (value);
+ dev->dev_no = bit;
+
+ dev->dev = device_create(phidget_class, &dev->udev->dev, MKDEV(0, 0),
+ dev, "servo%d", dev->dev_no);
+ if (IS_ERR(dev->dev)) {
+ rc = PTR_ERR(dev->dev);
+ dev->dev = NULL;
+ goto out;
+ }
+
+ servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1;
+
+ for (i=0; i<servo_count; i++) {
+ rc = device_create_file(dev->dev, &dev_attrs[i]);
+ if (rc)
+ goto out2;
+ }
+
+ dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n",
+ servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2);
+
+ if (!(dev->type & SERVO_VERSION_30))
+ dev_info(&interface->dev,
+ "WARNING: v2.0 not tested! Please report if it works.\n");
+
+ return 0;
+out2:
+ while (i-- > 0)
+ device_remove_file(dev->dev, &dev_attrs[i]);
+out:
+ if (dev) {
+ if (dev->dev)
+ device_unregister(dev->dev);
+ if (dev->dev_no >= 0)
+ clear_bit(dev->dev_no, &device_no);
+
+ kfree(dev);
+ }
+
+ return rc;
+}
+
+static void
+servo_disconnect(struct usb_interface *interface)
+{
+ struct phidget_servo *dev;
+ int servo_count, i;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ if (!dev)
+ return;
+
+ servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1;
+
+ for (i=0; i<servo_count; i++)
+ device_remove_file(dev->dev, &dev_attrs[i]);
+
+ device_unregister(dev->dev);
+ usb_put_dev(dev->udev);
+
+ dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n",
+ servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2);
+
+ clear_bit(dev->dev_no, &device_no);
+ kfree(dev);
+}
+
+static struct usb_driver servo_driver = {
+ .name = "phidgetservo",
+ .probe = servo_probe,
+ .disconnect = servo_disconnect,
+ .id_table = id_table
+};
+
+static int __init
+phidget_servo_init(void)
+{
+ int retval;
+
+ retval = usb_register(&servo_driver);
+ if (retval)
+ err("usb_register failed. Error number %d", retval);
+
+ return retval;
+}
+
+static void __exit
+phidget_servo_exit(void)
+{
+ usb_deregister(&servo_driver);
+}
+
+module_init(phidget_servo_init);
+module_exit(phidget_servo_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/mm/page_io.c b/mm/page_io.c
index 82040c591dd4..0bf30bd4b0e6 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -150,10 +150,15 @@ out:
return ret;
}
+/* this comment ensure the patch applies to swap_sync_page
+ * and not swap_set_page_dirty by mistake
+ */
void swap_sync_page(struct page *page)
{
struct swap_info_struct *sis = page_swap_info(page);
+ if (!sis)
+ return;
if (sis->flags & SWP_FILE) {
struct address_space *mapping = sis->swap_file->f_mapping;
@@ -168,8 +173,6 @@ int swap_set_page_dirty(struct page *page)
{
struct swap_info_struct *sis = page_swap_info(page);
- if (!sis)
- return;
if (sis->flags & SWP_FILE) {
struct address_space *mapping = sis->swap_file->f_mapping;
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore
deleted file mode 100644
index 0a0a99f3b083..000000000000
--- a/security/apparmor/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-#
-# Generated include files
-#
-af_names.h
-capability_names.h
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 5796bcfeaeca..95f75a8d523f 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -19,7 +19,7 @@
#include <linux/uaccess.h>
#include <linux/namei.h>
-#include "include/security/apparmor.h"
+#include "include/apparmor.h"
#include "include/audit.h"
#include "include/context.h"
#include "include/policy.h"
@@ -98,7 +98,6 @@ static struct aa_profile *next_profile(struct aa_profile *profile)
read_lock(&ns->base.lock);
return list_first_entry(&ns->base.profiles, struct aa_profile,
base.list);
- read_unlock(&ns->base.lock);
}
return NULL;
}
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index bd3a5c1fd9f2..a207d2823d04 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -15,7 +15,7 @@
#include <linux/audit.h>
#include <linux/socket.h>
-#include "include/security/apparmor.h"
+#include "include/apparmor.h"
#include "include/audit.h"
#include "include/policy.h"
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c
index e79de20cfa6b..5bb2eca6204d 100644
--- a/security/apparmor/capability.c
+++ b/security/apparmor/capability.c
@@ -16,7 +16,7 @@
#include <linux/errno.h>
#include <linux/gfp.h>
-#include "include/security/apparmor.h"
+#include "include/apparmor.h"
#include "include/capability.h"
#include "include/context.h"
#include "include/policy.h"
@@ -72,6 +72,7 @@ static int aa_audit_caps(struct aa_profile *profile, struct aa_audit_caps *sa)
/* Do simple duplicate message elimination */
ent = &get_cpu_var(audit_cache);
if (sa->base.task == ent->task && cap_raised(ent->caps, sa->cap)) {
+ put_cpu_var(audit_cache);
if (PROFILE_COMPLAIN(profile))
return 0;
return sa->base.error;
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index af8b132d409a..497176f5c00c 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -21,7 +21,7 @@
#include <linux/personality.h>
#include "include/audit.h"
-#include "include/security/apparmorfs.h"
+#include "include/apparmorfs.h"
#include "include/context.h"
#include "include/domain.h"
#include "include/file.h"
@@ -64,11 +64,13 @@ static int aa_may_change_ptraced_domain(struct task_struct *task,
cred = aa_get_task_policy(tracer, &tracerp);
rcu_read_unlock();
- if (!tracerp)
- return error;
+ if (!tracer || !tracerp)
+ goto out;
error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH);
- put_cred(cred);
+out:
+ if (cred)
+ put_cred(cred);
return error;
}
@@ -224,7 +226,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
unsigned int state = DFA_START;
struct aa_audit_file sa;
struct path_cond cond = { bprm->file->f_path.dentry->d_inode->i_uid,
- bprm->file->f_path.dentry->d_inode->i_mode };
+ bprm->file->f_path.dentry->d_inode->i_mode };
sa.base.error = cap_bprm_set_creds(bprm);
if (sa.base.error)
@@ -248,7 +250,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
sa.base.error = aa_get_name(&bprm->file->f_path, 0, &buffer,
(char **) &sa.name);
if (sa.base.error) {
- if (profile || profile->flags & PFLAG_IX_ON_NAME_ERROR)
+ if (!profile || profile->flags & PFLAG_IX_ON_NAME_ERROR)
sa.base.error = 0;
sa.base.info = "Exec failed name resolution";
sa.name = bprm->filename;
@@ -374,7 +376,7 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm)
/* the decision to use secure exec is computed in set_creds
* and stored in bprm->unsafe. The AppArmor X_UNSAFE flag is
- * indicates don't
+ * indicates don't
*/
if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED))
ret = 1;
@@ -430,7 +432,7 @@ static void revalidate_file(struct aa_profile *profile, struct file *file,
}
}
-/*
+/*
* derived from security/selinux/hooks.c: flush_unauthorized_files &&
* fs/exec.c:flush_old_files
*/
@@ -470,7 +472,7 @@ static int revalidate_files(struct aa_profile *profile,
}
spin_lock(&files->file_lock);
}
- spin_unlock(&files->file_lock);
+ spin_unlock(&files->file_lock);
kfree(buffer);
return 0;
}
@@ -525,15 +527,16 @@ int aa_change_hat(const char *hat_name, u64 token, int permtest)
struct aa_task_context *cxt;
struct aa_profile *profile, *previous_profile, *hat = NULL;
struct aa_audit_file sa;
+ char *name = NULL;
memset(&sa, 0, sizeof(sa));
sa.base.gfp_mask = GFP_KERNEL;
sa.base.operation = "change_hat";
+ sa.request = AA_MAY_CHANGEHAT;
cred = aa_current_policy(&profile);
cxt = cred->security;
previous_profile = cxt->sys.previous;
- token = cxt->sys.token;
if (!profile) {
sa.base.info = "unconfined";
@@ -542,32 +545,36 @@ int aa_change_hat(const char *hat_name, u64 token, int permtest)
}
if (hat_name) {
- if (previous_profile)
- sa.name = previous_profile->fqname;
- else
- sa.name = profile->fqname;
-
+ struct aa_profile *root;
+ root = PROFILE_IS_HAT(profile) ? profile->parent : profile;
sa.name2 = profile->ns->base.name;
- if (PROFILE_IS_HAT(profile))
- hat = aa_find_child(profile->parent, hat_name);
- else
- hat = aa_find_child(profile, hat_name);
+ hat = aa_find_child(root, hat_name);
if (!hat) {
+ if (permtest || !PROFILE_COMPLAIN(root))
+ /* probing is an expected unfortunate behavior
+ * of the change_hat api is traditionally quiet
+ */
+ goto out;
+
+ name = new_compound_name(root->fqname, hat_name);
+
+ sa.name = name;
sa.base.info = "hat not found";
sa.base.error = -ENOENT;
- if (permtest || !PROFILE_COMPLAIN(profile))
- goto audit;
hat = aa_alloc_null_profile(profile, 1);
if (!hat) {
sa.base.info = "failed null profile create";
sa.base.error = -ENOMEM;
goto audit;
}
- } else if (!PROFILE_IS_HAT(hat)) {
- sa.base.info = "target not hat";
- sa.base.error = -EPERM;
- goto audit;
+ } else {
+ sa.name = hat->fqname;
+ if (!PROFILE_IS_HAT(hat)) {
+ sa.base.info = "target not hat";
+ sa.base.error = -EPERM;
+ goto audit;
+ }
}
sa.base.error = aa_may_change_ptraced_domain(current, hat);
@@ -579,27 +586,27 @@ int aa_change_hat(const char *hat_name, u64 token, int permtest)
if (!permtest) {
sa.base.error = aa_set_current_hat(hat, token);
- if (sa.base.error == -EACCES) {
- (void)send_sig_info(SIGKILL, NULL, current);
- sa.base.error = aa_audit(AUDIT_APPARMOR_KILL,
- profile, &sa.base,
- file_audit_cb);
- goto out;
- }
+ if (sa.base.error == -EACCES)
+ sa.perms.kill = AA_MAY_CHANGEHAT;
+ else if (name && !sa.base.error)
+ /* reset error for learning of new hats */
+ sa.base.error = -ENOENT;
}
- } else if (previous_profile)
+ } else if (previous_profile) {
+ sa.name = previous_profile->fqname;
sa.base.error = aa_restore_previous_profile(token);
- /* else
- ignore restores when there is no saved profile
- */
+ sa.perms.kill = AA_MAY_CHANGEHAT;
+ } else
+ /* ignore restores when there is no saved profile */
+ goto out;
audit:
if (!permtest)
sa.base.error = aa_audit_file(profile, &sa);
-
out:
aa_put_profile(hat);
+ kfree(name);
return sa.base.error;
}
@@ -677,7 +684,7 @@ int aa_change_profile(const char *ns_name, const char *fqname, int onexec,
goto audit;
target = aa_alloc_null_profile(profile, 0);
}
-
+
/* check if tracing task is allowed to trace target domain */
sa.base.error = aa_may_change_ptraced_domain(current, target);
if (sa.base.error) {
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index b52566ce39b3..f27bae61b9b8 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -12,7 +12,7 @@
* License.
*/
-#include "include/security/apparmor.h"
+#include "include/apparmor.h"
#include "include/audit.h"
#include "include/file.h"
#include "include/match.h"
@@ -139,18 +139,17 @@ int aa_audit_file(struct aa_profile *profile, struct aa_audit_file *sa)
return 0;
type = AUDIT_APPARMOR_AUDIT;
} else {
- /* quiet auditing of specific known rejects */
- u16 mask = sa->perms.quiet;
- u16 denied = sa->request & ~sa->perms.allowed;
+ /* only report permissions that were denied */
+ sa->request = sa->request & ~sa->perms.allowed;
- if (denied & sa->perms.kill)
+ if (sa->request & sa->perms.kill)
type = AUDIT_APPARMOR_KILL;
- /* assumes quiet and kill do not overlap */
- if ((denied & mask) &&
+ /* quiet known rejects, assumes quiet and kill do not overlap */
+ if ((sa->request & sa->perms.quiet) &&
PROFILE_AUDIT_MODE(profile) != AUDIT_NOQUIET &&
PROFILE_AUDIT_MODE(profile) != AUDIT_ALL)
- sa->request &= ~mask;
+ sa->request &= ~sa->perms.quiet;
if (!sa->request)
return PROFILE_COMPLAIN(profile) ? 0 : sa->base.error;
diff --git a/security/apparmor/include/security/apparmor.h b/security/apparmor/include/apparmor.h
index 28c6fc260c41..fbbc96169fd2 100644
--- a/security/apparmor/include/security/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -18,7 +18,7 @@
#include <linux/fs.h>
/* Control parameters settable thru module/boot flags or
- * via /sys/kernel/securitysecurity/apparmor/control */
+ * via /sys/kernel/security/apparmor/control */
extern enum audit_mode g_apparmor_audit;
extern int g_apparmor_audit_header;
extern int g_apparmor_debug;
diff --git a/security/apparmor/include/security/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index 1af7723ab735..1af7723ab735 100644
--- a/security/apparmor/include/security/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h
index 14c0dfee2814..cd3bdaa2e06f 100644
--- a/security/apparmor/include/file.h
+++ b/security/apparmor/include/file.h
@@ -54,7 +54,7 @@ struct aa_profile;
#define AA_X_INDEX_MASK 0x03ff
#define AA_X_TYPE_MASK 0x0c00
-#define AA_X_TYPE_SHIFT 10
+#define AA_X_TYPE_SHIFT 10
#define AA_X_NONE 0x0000
#define AA_X_NAME 0x0400 /* use executable name px */
#define AA_X_TABLE 0x0800 /* use a specified name ->n# */
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index de26f9a10dea..c8c882f66172 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -22,7 +22,7 @@
#include <linux/slab.h>
#include <linux/socket.h>
-#include "security/apparmor.h"
+#include "apparmor.h"
#include "audit.h"
#include "capability.h"
#include "domain.h"
@@ -189,7 +189,7 @@ extern rwlock_t ns_list_lock;
extern struct aa_namespace *default_namespace;
extern enum profile_mode g_profile_mode;
-
+
void aa_add_profile(struct aa_policy_common *common,
struct aa_profile *profile);
@@ -201,7 +201,7 @@ void free_aa_namespace_kref(struct kref *kref);
void free_aa_namespace(struct aa_namespace *ns);
struct aa_namespace *__aa_find_namespace(struct list_head *head,
const char *name);
-
+
struct aa_namespace *aa_find_namespace(const char *name);
struct aa_namespace *aa_prepare_namespace(const char *name);
void aa_remove_namespace(struct aa_namespace *ns);
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 4ea9b32223d5..5df94b6bea6a 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -24,8 +24,8 @@
#include <linux/audit.h>
#include <net/sock.h>
-#include "include/security/apparmor.h"
-#include "include/security/apparmorfs.h"
+#include "include/apparmor.h"
+#include "include/apparmorfs.h"
#include "include/audit.h"
#include "include/capability.h"
#include "include/context.h"
@@ -128,7 +128,7 @@ static int apparmor_sysctl(struct ctl_table *table, int op)
int mask;
mask = 0;
- if (op & 4)
+ if (op & 4)
mask |= MAY_READ;
if (op & 2)
mask |= MAY_WRITE;
@@ -138,7 +138,7 @@ static int apparmor_sysctl(struct ctl_table *table, int op)
if (!buffer)
goto out;
- /*
+ /*
* TODO: convert this over to using a global or per
* namespace control instead of a hard coded /proc
*/
@@ -171,7 +171,7 @@ static int common_perm(const char *op, struct path *path, u16 mask,
}
static int common_perm_dentry(const char *op, struct path *dir,
- struct dentry *dentry, u16 mask,
+ struct dentry *dentry, u16 mask,
struct path_cond *cond)
{
struct path path = { dir->mnt, dentry };
@@ -281,7 +281,7 @@ static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry,
if (!error)
error = aa_path_perm(profile, "rename_dest", &new_path,
AA_MAY_CREATE | MAY_WRITE, &cond);
-
+
}
return error;
}
@@ -290,7 +290,7 @@ static int apparmor_dentry_open(struct file *file, const struct cred *cred)
{
struct aa_profile *profile;
int error = 0;
-
+
/* If in exec permission is handled by bprm hooks */
if (current->in_execve ||
!mediated_filesystem(file->f_path.dentry->d_inode))
@@ -299,7 +299,7 @@ static int apparmor_dentry_open(struct file *file, const struct cred *cred)
aa_cred_policy(cred, &profile);
if (profile) {
struct aa_file_cxt *fcxt = file->f_security;
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct inode *inode = file->f_path.dentry->d_inode;
struct path_cond cond = { inode->i_uid, inode->i_mode };
error = aa_path_perm(profile, "open", &file->f_path,
@@ -369,7 +369,7 @@ static int common_file_perm(const char *op, struct file *file, u16 mask)
if (!fprofile || !file->f_path.mnt ||
!mediated_filesystem(file->f_path.dentry->d_inode))
- return 0;
+ return 0;
profile = aa_current_profile_wupd();
if (profile && ((fprofile != profile) || (mask & ~fcxt->allowed)))
@@ -391,7 +391,7 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd)
/*
* AppArmor doesn't current use the fcntl hook.
- *
+ *
* FIXME - these are not implemented yet - REMOVE file_fcntl hook
* NOTE: some of the file control commands are further mediated
* by other hooks
@@ -547,15 +547,16 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
return error;
}
-static int apparmor_task_setrlimit(unsigned int resource,
+static int apparmor_task_setrlimit(struct task_struct *task,
+ unsigned int resource,
struct rlimit *new_rlim)
{
+ struct rlimit *old_rlim = task->signal->rlim + resource;
struct aa_profile *profile = aa_current_profile_wupd();
int error = 0;
- if (profile) {
+ if (profile && old_rlim->rlim_max != new_rlim->rlim_max)
error = aa_task_setrlimit(profile, resource, new_rlim);
- }
return error;
}
@@ -762,7 +763,7 @@ static int param_set_mode(const char *val, struct kernel_param *kp);
static int param_get_mode(char *buffer, struct kernel_param *kp);
#define param_check_mode(name, p) __param_check(name, p, int)
-/* Flag values, also controllable via /sys/modulesecurity/apparmor/parameters
+/* Flag values, also controllable via /sys/module/apparmor/parameters
* We define special types as we want to do additional mediation.
*/
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 6d219955eb6a..1a50309f9734 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -12,17 +12,27 @@
* License.
*/
+#include <linux/errno.h>
#include <linux/kernel.h>
+#include <linux/mm.h>
#include <linux/slab.h>
-#include <linux/errno.h>
+#include <linux/vmalloc.h>
/* TODO: remove !!!! */
// #include <linux/fs.h>
-#include "include/security/apparmor.h"
+#include "include/apparmor.h"
#include "include/match.h"
#include "include/file.h"
+static void free_table(struct table_header *table)
+{
+ if (is_vmalloc_addr(table))
+ vfree(table);
+ else
+ kfree(table);
+}
+
static struct table_header *unpack_table(void *blob, size_t bsize)
{
struct table_header *table = NULL;
@@ -46,6 +56,8 @@ static struct table_header *unpack_table(void *blob, size_t bsize)
goto out;
table = kmalloc(tsize, GFP_KERNEL);
+ if (!table)
+ table = vmalloc(tsize);
if (table) {
*table = th;
if (th.td_flags == YYTD_DATA8)
@@ -110,7 +122,7 @@ int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size)
goto fail;
break;
default:
- kfree(table);
+ free_table(table);
goto fail;
}
@@ -122,7 +134,7 @@ int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size)
fail:
for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) {
- kfree(dfa->tables[i]);
+ free_table(dfa->tables[i]);
dfa->tables[i] = NULL;
}
return error;
@@ -207,7 +219,7 @@ void aa_match_free(struct aa_dfa *dfa)
int i;
for (i = 0; i < ARRAY_SIZE(dfa->tables); i++)
- kfree(dfa->tables[i]);
+ free_table(dfa->tables[i]);
}
kfree(dfa);
}
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
index 010bb3b4d214..b236d14df40d 100644
--- a/security/apparmor/net.c
+++ b/security/apparmor/net.c
@@ -12,7 +12,7 @@
* License.
*/
-#include "include/security/apparmor.h"
+#include "include/apparmor.h"
#include "include/audit.h"
#include "include/context.h"
#include "include/net.h"
@@ -62,7 +62,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
audit_log_format(ab, " protocol=%d", sa->protocol);
}
-
+
}
static int aa_audit_net(struct aa_profile *profile, struct aa_audit_net *sa)
diff --git a/security/apparmor/path.c b/security/apparmor/path.c
index 3d8115a5ffcb..a48b5f4d813c 100644
--- a/security/apparmor/path.c
+++ b/security/apparmor/path.c
@@ -20,7 +20,7 @@
#include <linux/slab.h>
#include <linux/fs_struct.h>
-#include "include/security/apparmor.h"
+#include "include/apparmor.h"
#include "include/path.h"
int aa_get_name_to_buffer(struct path *path, int is_dir, char *buffer, int size,
@@ -88,6 +88,7 @@ int d_namespace_path(struct path *path, char *buf, int buflen, char **name)
{
struct path root, tmp, ns_root = { };
char *res;
+ int deleted;
int error = 0;
read_lock(&current->fs->lock);
@@ -101,8 +102,12 @@ int d_namespace_path(struct path *path, char *buf, int buflen, char **name)
ns_root.dentry = dget(ns_root.mnt->mnt_root);
spin_unlock(&vfsmount_lock);
spin_lock(&dcache_lock);
- tmp = ns_root;
- res = __d_path(path, &tmp, buf, buflen);
+
+ do {
+ tmp = ns_root;
+ deleted = d_unlinked(path->dentry);
+ res = __d_path(path, &tmp, buf, buflen);
+ } while (deleted != d_unlinked(path->dentry));
*name = res;
/* handle error conditions - and still allow a partial path to
@@ -110,6 +115,29 @@ int d_namespace_path(struct path *path, char *buf, int buflen, char **name)
if (IS_ERR(res)) {
error = PTR_ERR(res);
*name = buf;
+ } else if (deleted) {
+ /* The stripping of (deleted) is a hack that could be removed
+ * with an updated __d_path
+ */
+
+ /* Currently 2 cases fall into here. Fixing the mediation
+ * of deleted files for things like trunc.
+ * And the newly allocated dentry case. The first case
+ * means we strip deleted for everything so the new
+ * dentry test case is commented out below.
+ */
+ buf[buflen - 11] = 0; /* - (len(" (deleted)") +\0) */
+
+ /* if (!path->dentry->d_inode) {
+ * On some filesystems, newly allocated dentries appear
+ * to the security_path hooks as a deleted
+ * dentry except without an inode allocated.
+ *
+ * Remove the appended deleted text and return as a
+ * string for normal mediation. The (deleted) string
+ * is guarenteed to be added in this case, so just
+ * strip it.
+ */
} else if (!IS_ROOT(path->dentry) && d_unhashed(path->dentry)) {
error = -ENOENT;
#if 0
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index e7b0b61cac9a..e2bc9e5afe95 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -47,7 +47,7 @@
#include <linux/spinlock.h>
#include <linux/string.h>
-#include "include/security/apparmor.h"
+#include "include/apparmor.h"
#include "include/capability.h"
#include "include/file.h"
#include "include/ipc.h"
@@ -105,7 +105,7 @@ static void common_free(struct aa_policy_common *common)
static struct aa_policy_common *__common_find(struct list_head *head,
const char *name)
-
+
{
struct aa_policy_common *common;
@@ -228,7 +228,7 @@ void free_aa_namespace(struct aa_namespace *ns)
struct aa_namespace *__aa_find_namespace(struct list_head *head,
const char *name)
-
+
{
return (struct aa_namespace *) __common_find(head, name);
}
@@ -276,7 +276,7 @@ struct aa_namespace *aa_find_namespace_by_strn(const char *name, int len)
struct aa_namespace *aa_prepare_namespace(const char *name)
{
struct aa_namespace *ns;
-
+
write_lock(&ns_list_lock);
if (name)
ns = aa_get_namespace(__aa_find_namespace(&ns_list, name));
@@ -292,7 +292,7 @@ struct aa_namespace *aa_prepare_namespace(const char *name)
ns = __aa_find_namespace(&ns_list, name);
if (!ns) {
list_add(&new_ns->base.list, &ns_list);
- ns = new_ns;
+ ns = aa_get_namespace(new_ns);
} else {
/* raced so free the new one */
free_aa_namespace(new_ns);
@@ -322,7 +322,7 @@ void __aa_remove_profile(struct aa_profile *profile,
if (replacement)
profile->replacedby = aa_get_profile(replacement);
else
- profile->replacedby = ERR_PTR(-EINVAL);
+ profile->replacedby = aa_get_profile(profile->ns->unconfined);
list_del_init(&profile->base.list);
if (!(profile->flags & PFLAG_NO_LIST_REF))
aa_put_profile(profile);
@@ -417,12 +417,12 @@ struct aa_profile *alloc_aa_profile(const char *fqname)
profile = kzalloc(sizeof(*profile), GFP_KERNEL);
if (!profile)
return NULL;
-
+
if (!common_init(&profile->base, fqname)) {
kfree(profile);
return NULL;
}
-
+
profile->fqname = profile->base.name;
profile->base.name = (char *) fqname_subname((const char *) profile->fqname);
return profile;
@@ -541,7 +541,7 @@ void free_aa_profile(struct aa_profile *profile)
aa_free_sid(profile->sid);
aa_match_free(profile->xmatch);
- if (profile->replacedby && !PTR_ERR(profile->replacedby))
+ if (profile->replacedby)
aa_put_profile(profile->replacedby);
memset(profile, 0, sizeof(profile));
@@ -593,7 +593,7 @@ struct aa_policy_common *__aa_find_parent_by_fqname(struct aa_namespace *ns,
common = &ns->base;
-
+
for (split = strstr(fqname, "//"); split; ) {
profile = __aa_find_profile_by_strn(&common->profiles, fqname,
split - fqname);
@@ -715,13 +715,7 @@ struct aa_profile *aa_sys_find_attach(struct aa_policy_common *base,
struct aa_profile *aa_profile_newest(struct aa_profile *profile)
{
if (unlikely(profile && profile->replacedby)) {
- for (;profile->replacedby; profile = profile->replacedby) {
- if (IS_ERR(profile->replacedby)) {
- /* profile has been removed */
- profile = NULL;
- break;
- }
- }
+ for (;profile->replacedby; profile = profile->replacedby) ;
}
return profile;
diff --git a/security/apparmor/policy_interface.c b/security/apparmor/policy_interface.c
index 420509769c24..76093541dfcf 100644
--- a/security/apparmor/policy_interface.c
+++ b/security/apparmor/policy_interface.c
@@ -20,7 +20,7 @@
#include <asm/unaligned.h>
#include <linux/errno.h>
-#include "include/security/apparmor.h"
+#include "include/apparmor.h"
#include "include/audit.h"
#include "include/context.h"
#include "include/match.h"
@@ -376,6 +376,7 @@ static int aa_unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
if (!profile->file.trans.table)
goto fail;
+ profile->file.trans.size = size;
for (i = 0; i < size; i++) {
char *tmp;
if (!aa_is_dynstring(e, &tmp, NULL))
@@ -389,11 +390,15 @@ static int aa_unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
goto fail;
if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
goto fail;
- profile->file.trans.size = size;
}
return 1;
fail:
+ if (profile->file.trans.table) {
+ int i;
+ for (i = 0; i < profile->file.trans.size; i++)
+ kfree(profile->file.trans.table[i]);
+ }
e->pos = pos;
return 0;
}
@@ -658,7 +663,6 @@ ssize_t aa_interface_add_profiles(void *data, size_t size)
aa_audit_iface(&sa);
aa_put_namespace(ns);
- kfree(e.ns_name);
return size;
fail2:
@@ -668,7 +672,6 @@ fail:
error = aa_audit_iface(&sa);
aa_put_namespace(ns);
aa_put_profile(profile);
- kfree(e.ns_name);
return error;
}
@@ -713,7 +716,7 @@ ssize_t aa_interface_replace_profiles(void *udata, size_t size)
sa.base.info = "failed to prepare namespace";
sa.base.error = -ENOMEM;
goto fail;
- }
+ }
sa.name = new_profile->fqname;
@@ -771,7 +774,6 @@ ssize_t aa_interface_replace_profiles(void *udata, size_t size)
aa_audit_iface(&sa);
aa_put_namespace(ns);
aa_put_profile(old_profile);
- kfree(e.ns_name);
return size;
fail2:
@@ -781,7 +783,6 @@ fail:
aa_put_namespace(ns);
aa_put_profile(old_profile);
aa_put_profile(new_profile);
- kfree(e.ns_name);
return error;
}
@@ -822,7 +823,10 @@ ssize_t aa_interface_remove_profiles(char *name, size_t size)
write_lock(&ns->base.lock);
if (!name) {
/* remove namespace */
- // __aa_remove_namespace(ns);
+ if (ns == default_namespace)
+ __aa_profile_list_release(&ns->base.profiles);
+ else
+ __aa_remove_namespace(ns);
} else {
/* remove profile */
profile = __aa_find_profile_by_fqname(ns, name);
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c
index 40ae475f3d0e..834cfab49332 100644
--- a/security/apparmor/procattr.c
+++ b/security/apparmor/procattr.c
@@ -12,7 +12,7 @@
* License.
*/
-#include "include/security/apparmor.h"
+#include "include/apparmor.h"
#include "include/policy.h"
#include "include/domain.h"
diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c
index f2c0ce7dd53d..aa41a351f6b0 100644
--- a/security/apparmor/sid.c
+++ b/security/apparmor/sid.c
@@ -50,13 +50,13 @@ u32 aa_alloc_sid(int is_usr)
{
u32 sid;
- /*
+ /*
* TODO FIXME: sid recycling - part of profile mapping table
*/
spin_lock(&sid_lock);
if (is_usr) {
sid = (++global_usr_sid) << 16;
-
+
} else {
sid = ++global_sys_sid;
}