diff options
Diffstat (limited to 'drivers/char/ipmi')
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index df7dbbff57ae..2ace62b1d326 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c | |||
@@ -51,6 +51,7 @@ | |||
51 | #include <linux/list.h> | 51 | #include <linux/list.h> |
52 | #include <linux/pci.h> | 52 | #include <linux/pci.h> |
53 | #include <linux/ioport.h> | 53 | #include <linux/ioport.h> |
54 | #include <linux/notifier.h> | ||
54 | #include <asm/irq.h> | 55 | #include <asm/irq.h> |
55 | #ifdef CONFIG_HIGH_RES_TIMERS | 56 | #ifdef CONFIG_HIGH_RES_TIMERS |
56 | #include <linux/hrtime.h> | 57 | #include <linux/hrtime.h> |
@@ -222,6 +223,12 @@ struct smi_info | |||
222 | unsigned long incoming_messages; | 223 | unsigned long incoming_messages; |
223 | }; | 224 | }; |
224 | 225 | ||
226 | static struct notifier_block *xaction_notifier_list; | ||
227 | static int register_xaction_notifier(struct notifier_block * nb) | ||
228 | { | ||
229 | return notifier_chain_register(&xaction_notifier_list, nb); | ||
230 | } | ||
231 | |||
225 | static void si_restart_short_timer(struct smi_info *smi_info); | 232 | static void si_restart_short_timer(struct smi_info *smi_info); |
226 | 233 | ||
227 | static void deliver_recv_msg(struct smi_info *smi_info, | 234 | static void deliver_recv_msg(struct smi_info *smi_info, |
@@ -281,6 +288,11 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) | |||
281 | do_gettimeofday(&t); | 288 | do_gettimeofday(&t); |
282 | printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec); | 289 | printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec); |
283 | #endif | 290 | #endif |
291 | err = notifier_call_chain(&xaction_notifier_list, 0, smi_info); | ||
292 | if (err & NOTIFY_STOP_MASK) { | ||
293 | rv = SI_SM_CALL_WITHOUT_DELAY; | ||
294 | goto out; | ||
295 | } | ||
284 | err = smi_info->handlers->start_transaction( | 296 | err = smi_info->handlers->start_transaction( |
285 | smi_info->si_sm, | 297 | smi_info->si_sm, |
286 | smi_info->curr_msg->data, | 298 | smi_info->curr_msg->data, |
@@ -291,6 +303,7 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) | |||
291 | 303 | ||
292 | rv = SI_SM_CALL_WITHOUT_DELAY; | 304 | rv = SI_SM_CALL_WITHOUT_DELAY; |
293 | } | 305 | } |
306 | out: | ||
294 | spin_unlock(&(smi_info->msg_lock)); | 307 | spin_unlock(&(smi_info->msg_lock)); |
295 | 308 | ||
296 | return rv; | 309 | return rv; |
@@ -2080,6 +2093,71 @@ static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info) | |||
2080 | } | 2093 | } |
2081 | } | 2094 | } |
2082 | 2095 | ||
2096 | #define CANNOT_RETURN_REQUESTED_LENGTH 0xCA | ||
2097 | static void return_hosed_msg_badsize(struct smi_info *smi_info) | ||
2098 | { | ||
2099 | struct ipmi_smi_msg *msg = smi_info->curr_msg; | ||
2100 | |||
2101 | /* Make it a reponse */ | ||
2102 | msg->rsp[0] = msg->data[0] | 4; | ||
2103 | msg->rsp[1] = msg->data[1]; | ||
2104 | msg->rsp[2] = CANNOT_RETURN_REQUESTED_LENGTH; | ||
2105 | msg->rsp_size = 3; | ||
2106 | smi_info->curr_msg = NULL; | ||
2107 | deliver_recv_msg(smi_info, msg); | ||
2108 | } | ||
2109 | |||
2110 | /* | ||
2111 | * dell_poweredge_bt_xaction_handler | ||
2112 | * @info - smi_info.device_id must be populated | ||
2113 | * | ||
2114 | * Dell PowerEdge servers with the BT interface (x6xx and 1750) will | ||
2115 | * not respond to a Get SDR command if the length of the data | ||
2116 | * requested is exactly 0x3A, which leads to command timeouts and no | ||
2117 | * data returned. This intercepts such commands, and causes userspace | ||
2118 | * callers to try again with a different-sized buffer, which succeeds. | ||
2119 | */ | ||
2120 | |||
2121 | #define STORAGE_NETFN 0x0A | ||
2122 | #define STORAGE_CMD_GET_SDR 0x23 | ||
2123 | static int dell_poweredge_bt_xaction_handler(struct notifier_block *self, | ||
2124 | unsigned long unused, | ||
2125 | void *in) | ||
2126 | { | ||
2127 | struct smi_info *smi_info = in; | ||
2128 | unsigned char *data = smi_info->curr_msg->data; | ||
2129 | unsigned int size = smi_info->curr_msg->data_size; | ||
2130 | if (size >= 8 && | ||
2131 | (data[0]>>2) == STORAGE_NETFN && | ||
2132 | data[1] == STORAGE_CMD_GET_SDR && | ||
2133 | data[7] == 0x3A) { | ||
2134 | return_hosed_msg_badsize(smi_info); | ||
2135 | return NOTIFY_STOP; | ||
2136 | } | ||
2137 | return NOTIFY_DONE; | ||
2138 | } | ||
2139 | |||
2140 | static struct notifier_block dell_poweredge_bt_xaction_notifier = { | ||
2141 | .notifier_call = dell_poweredge_bt_xaction_handler, | ||
2142 | }; | ||
2143 | |||
2144 | /* | ||
2145 | * setup_dell_poweredge_bt_xaction_handler | ||
2146 | * @info - smi_info.device_id must be filled in already | ||
2147 | * | ||
2148 | * Fills in smi_info.device_id.start_transaction_pre_hook | ||
2149 | * when we know what function to use there. | ||
2150 | */ | ||
2151 | static void | ||
2152 | setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info) | ||
2153 | { | ||
2154 | struct ipmi_device_id *id = &smi_info->device_id; | ||
2155 | const char mfr[3]=DELL_IANA_MFR_ID; | ||
2156 | if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr)) && | ||
2157 | smi_info->si_type == SI_BT) | ||
2158 | register_xaction_notifier(&dell_poweredge_bt_xaction_notifier); | ||
2159 | } | ||
2160 | |||
2083 | /* | 2161 | /* |
2084 | * setup_oem_data_handler | 2162 | * setup_oem_data_handler |
2085 | * @info - smi_info.device_id must be filled in already | 2163 | * @info - smi_info.device_id must be filled in already |
@@ -2093,6 +2171,11 @@ static void setup_oem_data_handler(struct smi_info *smi_info) | |||
2093 | setup_dell_poweredge_oem_data_handler(smi_info); | 2171 | setup_dell_poweredge_oem_data_handler(smi_info); |
2094 | } | 2172 | } |
2095 | 2173 | ||
2174 | static void setup_xaction_handlers(struct smi_info *smi_info) | ||
2175 | { | ||
2176 | setup_dell_poweredge_bt_xaction_handler(smi_info); | ||
2177 | } | ||
2178 | |||
2096 | /* Returns 0 if initialized, or negative on an error. */ | 2179 | /* Returns 0 if initialized, or negative on an error. */ |
2097 | static int init_one_smi(int intf_num, struct smi_info **smi) | 2180 | static int init_one_smi(int intf_num, struct smi_info **smi) |
2098 | { | 2181 | { |
@@ -2188,6 +2271,7 @@ static int init_one_smi(int intf_num, struct smi_info **smi) | |||
2188 | goto out_err; | 2271 | goto out_err; |
2189 | 2272 | ||
2190 | setup_oem_data_handler(new_smi); | 2273 | setup_oem_data_handler(new_smi); |
2274 | setup_xaction_handlers(new_smi); | ||
2191 | 2275 | ||
2192 | /* Try to claim any interrupts. */ | 2276 | /* Try to claim any interrupts. */ |
2193 | new_smi->irq_setup(new_smi); | 2277 | new_smi->irq_setup(new_smi); |