aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/libsas/Kconfig9
-rw-r--r--drivers/scsi/libsas/Makefile1
-rw-r--r--drivers/scsi/libsas/sas_expander.c8
-rw-r--r--drivers/scsi/libsas/sas_host_smp.c274
-rw-r--r--drivers/scsi/libsas/sas_internal.h14
5 files changed, 301 insertions, 5 deletions
diff --git a/drivers/scsi/libsas/Kconfig b/drivers/scsi/libsas/Kconfig
index c01a40d321d4..18f33cd54411 100644
--- a/drivers/scsi/libsas/Kconfig
+++ b/drivers/scsi/libsas/Kconfig
@@ -38,6 +38,15 @@ config SCSI_SAS_ATA
38 Builds in ATA support into libsas. Will necessitate 38 Builds in ATA support into libsas. Will necessitate
39 the loading of libata along with libsas. 39 the loading of libata along with libsas.
40 40
41config SCSI_SAS_HOST_SMP
42 bool "Support for SMP interpretation for SAS hosts"
43 default y
44 depends on SCSI_SAS_LIBSAS
45 help
46 Allows sas hosts to receive SMP frames. Selecting this
47 option builds an SMP interpreter into libsas. Say
48 N here if you want to save the few kb this consumes.
49
41config SCSI_SAS_LIBSAS_DEBUG 50config SCSI_SAS_LIBSAS_DEBUG
42 bool "Compile the SAS Domain Transport Attributes in debug mode" 51 bool "Compile the SAS Domain Transport Attributes in debug mode"
43 default y 52 default y
diff --git a/drivers/scsi/libsas/Makefile b/drivers/scsi/libsas/Makefile
index fd387b91856e..60d6e93dd949 100644
--- a/drivers/scsi/libsas/Makefile
+++ b/drivers/scsi/libsas/Makefile
@@ -35,3 +35,4 @@ libsas-y += sas_init.o \
35 sas_expander.o \ 35 sas_expander.o \
36 sas_scsi_host.o 36 sas_scsi_host.o
37libsas-$(CONFIG_SCSI_SAS_ATA) += sas_ata.o 37libsas-$(CONFIG_SCSI_SAS_ATA) += sas_ata.o
38libsas-$(CONFIG_SCSI_SAS_HOST_SMP) += sas_host_smp.o \ No newline at end of file
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 27674fe468f5..76555b1ddfd5 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -1896,11 +1896,9 @@ int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
1896 } 1896 }
1897 1897
1898 /* no rphy means no smp target support (ie aic94xx host) */ 1898 /* no rphy means no smp target support (ie aic94xx host) */
1899 if (!rphy) { 1899 if (!rphy)
1900 printk("%s: can we send a smp request to a host?\n", 1900 return sas_smp_host_handler(shost, req, rsp);
1901 __FUNCTION__); 1901
1902 return -EINVAL;
1903 }
1904 type = rphy->identify.device_type; 1902 type = rphy->identify.device_type;
1905 1903
1906 if (type != SAS_EDGE_EXPANDER_DEVICE && 1904 if (type != SAS_EDGE_EXPANDER_DEVICE &&
diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c
new file mode 100644
index 000000000000..16f93123271e
--- /dev/null
+++ b/drivers/scsi/libsas/sas_host_smp.c
@@ -0,0 +1,274 @@
1/*
2 * Serial Attached SCSI (SAS) Expander discovery and configuration
3 *
4 * Copyright (C) 2007 James E.J. Bottomley
5 * <James.Bottomley@HansenPartnership.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; version 2 only.
10 */
11#include <linux/scatterlist.h>
12#include <linux/blkdev.h>
13
14#include "sas_internal.h"
15
16#include <scsi/scsi_transport.h>
17#include <scsi/scsi_transport_sas.h>
18#include "../scsi_sas_internal.h"
19
20static void sas_host_smp_discover(struct sas_ha_struct *sas_ha, u8 *resp_data,
21 u8 phy_id)
22{
23 struct sas_phy *phy;
24 struct sas_rphy *rphy;
25
26 if (phy_id >= sas_ha->num_phys) {
27 resp_data[2] = SMP_RESP_NO_PHY;
28 return;
29 }
30 resp_data[2] = SMP_RESP_FUNC_ACC;
31
32 phy = sas_ha->sas_phy[phy_id]->phy;
33 resp_data[9] = phy_id;
34 resp_data[13] = phy->negotiated_linkrate;
35 memcpy(resp_data + 16, sas_ha->sas_addr, SAS_ADDR_SIZE);
36 memcpy(resp_data + 24, sas_ha->sas_phy[phy_id]->attached_sas_addr,
37 SAS_ADDR_SIZE);
38 resp_data[40] = (phy->minimum_linkrate << 4) |
39 phy->minimum_linkrate_hw;
40 resp_data[41] = (phy->maximum_linkrate << 4) |
41 phy->maximum_linkrate_hw;
42
43 if (!sas_ha->sas_phy[phy_id]->port ||
44 !sas_ha->sas_phy[phy_id]->port->port_dev)
45 return;
46
47 rphy = sas_ha->sas_phy[phy_id]->port->port_dev->rphy;
48 resp_data[12] = rphy->identify.device_type << 4;
49 resp_data[14] = rphy->identify.initiator_port_protocols;
50 resp_data[15] = rphy->identify.target_port_protocols;
51}
52
53static void sas_report_phy_sata(struct sas_ha_struct *sas_ha, u8 *resp_data,
54 u8 phy_id)
55{
56 struct sas_rphy *rphy;
57 struct dev_to_host_fis *fis;
58 int i;
59
60 if (phy_id >= sas_ha->num_phys) {
61 resp_data[2] = SMP_RESP_NO_PHY;
62 return;
63 }
64
65 resp_data[2] = SMP_RESP_PHY_NO_SATA;
66
67 if (!sas_ha->sas_phy[phy_id]->port)
68 return;
69
70 rphy = sas_ha->sas_phy[phy_id]->port->port_dev->rphy;
71 fis = (struct dev_to_host_fis *)
72 sas_ha->sas_phy[phy_id]->port->port_dev->frame_rcvd;
73 if (rphy->identify.target_port_protocols != SAS_PROTOCOL_SATA)
74 return;
75
76 resp_data[2] = SMP_RESP_FUNC_ACC;
77 resp_data[9] = phy_id;
78 memcpy(resp_data + 16, sas_ha->sas_phy[phy_id]->attached_sas_addr,
79 SAS_ADDR_SIZE);
80
81 /* check to see if we have a valid d2h fis */
82 if (fis->fis_type != 0x34)
83 return;
84
85 /* the d2h fis is required by the standard to be in LE format */
86 for (i = 0; i < 20; i += 4) {
87 u8 *dst = resp_data + 24 + i, *src =
88 &sas_ha->sas_phy[phy_id]->port->port_dev->frame_rcvd[i];
89 dst[0] = src[3];
90 dst[1] = src[2];
91 dst[2] = src[1];
92 dst[3] = src[0];
93 }
94}
95
96static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id,
97 u8 phy_op, enum sas_linkrate min,
98 enum sas_linkrate max, u8 *resp_data)
99{
100 struct sas_internal *i =
101 to_sas_internal(sas_ha->core.shost->transportt);
102 struct sas_phy_linkrates rates;
103
104 if (phy_id >= sas_ha->num_phys) {
105 resp_data[2] = SMP_RESP_NO_PHY;
106 return;
107 }
108 switch (phy_op) {
109 case PHY_FUNC_NOP:
110 case PHY_FUNC_LINK_RESET:
111 case PHY_FUNC_HARD_RESET:
112 case PHY_FUNC_DISABLE:
113 case PHY_FUNC_CLEAR_ERROR_LOG:
114 case PHY_FUNC_CLEAR_AFFIL:
115 case PHY_FUNC_TX_SATA_PS_SIGNAL:
116 break;
117
118 default:
119 resp_data[2] = SMP_RESP_PHY_UNK_OP;
120 return;
121 }
122
123 rates.minimum_linkrate = min;
124 rates.maximum_linkrate = max;
125
126 if (i->dft->lldd_control_phy(sas_ha->sas_phy[phy_id], phy_op, &rates))
127 resp_data[2] = SMP_RESP_FUNC_FAILED;
128 else
129 resp_data[2] = SMP_RESP_FUNC_ACC;
130}
131
132int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
133 struct request *rsp)
134{
135 u8 *req_data = NULL, *resp_data = NULL, *buf;
136 struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
137 int error = -EINVAL, resp_data_len = rsp->data_len;
138
139 /* eight is the minimum size for request and response frames */
140 if (req->data_len < 8 || rsp->data_len < 8)
141 goto out;
142
143 if (bio_offset(req->bio) + req->data_len > PAGE_SIZE ||
144 bio_offset(rsp->bio) + rsp->data_len > PAGE_SIZE) {
145 shost_printk(KERN_ERR, shost,
146 "SMP request/response frame crosses page boundary");
147 goto out;
148 }
149
150 req_data = kzalloc(req->data_len, GFP_KERNEL);
151
152 /* make sure frame can always be built ... we copy
153 * back only the requested length */
154 resp_data = kzalloc(max(rsp->data_len, 128U), GFP_KERNEL);
155
156 if (!req_data || !resp_data) {
157 error = -ENOMEM;
158 goto out;
159 }
160
161 local_irq_disable();
162 buf = kmap_atomic(bio_page(req->bio), KM_USER0) + bio_offset(req->bio);
163 memcpy(req_data, buf, req->data_len);
164 kunmap_atomic(buf - bio_offset(req->bio), KM_USER0);
165 local_irq_enable();
166
167 if (req_data[0] != SMP_REQUEST)
168 goto out;
169
170 /* always succeeds ... even if we can't process the request
171 * the result is in the response frame */
172 error = 0;
173
174 /* set up default don't know response */
175 resp_data[0] = SMP_RESPONSE;
176 resp_data[1] = req_data[1];
177 resp_data[2] = SMP_RESP_FUNC_UNK;
178
179 switch (req_data[1]) {
180 case SMP_REPORT_GENERAL:
181 req->data_len -= 8;
182 resp_data_len -= 32;
183 resp_data[2] = SMP_RESP_FUNC_ACC;
184 resp_data[9] = sas_ha->num_phys;
185 break;
186
187 case SMP_REPORT_MANUF_INFO:
188 req->data_len -= 8;
189 resp_data_len -= 64;
190 resp_data[2] = SMP_RESP_FUNC_ACC;
191 memcpy(resp_data + 12, shost->hostt->name,
192 SAS_EXPANDER_VENDOR_ID_LEN);
193 memcpy(resp_data + 20, "libsas virt phy",
194 SAS_EXPANDER_PRODUCT_ID_LEN);
195 break;
196
197 case SMP_READ_GPIO_REG:
198 /* FIXME: need GPIO support in the transport class */
199 break;
200
201 case SMP_DISCOVER:
202 req->data_len =- 16;
203 if (req->data_len < 0) {
204 req->data_len = 0;
205 error = -EINVAL;
206 goto out;
207 }
208 resp_data_len -= 56;
209 sas_host_smp_discover(sas_ha, resp_data, req_data[9]);
210 break;
211
212 case SMP_REPORT_PHY_ERR_LOG:
213 /* FIXME: could implement this with additional
214 * libsas callbacks providing the HW supports it */
215 break;
216
217 case SMP_REPORT_PHY_SATA:
218 req->data_len =- 16;
219 if (req->data_len < 0) {
220 req->data_len = 0;
221 error = -EINVAL;
222 goto out;
223 }
224 resp_data_len -= 60;
225 sas_report_phy_sata(sas_ha, resp_data, req_data[9]);
226 break;
227
228 case SMP_REPORT_ROUTE_INFO:
229 /* Can't implement; hosts have no routes */
230 break;
231
232 case SMP_WRITE_GPIO_REG:
233 /* FIXME: need GPIO support in the transport class */
234 break;
235
236 case SMP_CONF_ROUTE_INFO:
237 /* Can't implement; hosts have no routes */
238 break;
239
240 case SMP_PHY_CONTROL:
241 req->data_len =- 44;
242 if (req->data_len < 0) {
243 req->data_len = 0;
244 error = -EINVAL;
245 goto out;
246 }
247 resp_data_len -= 8;
248 sas_phy_control(sas_ha, req_data[9], req_data[10],
249 req_data[32] >> 4, req_data[33] >> 4,
250 resp_data);
251 break;
252
253 case SMP_PHY_TEST_FUNCTION:
254 /* FIXME: should this be implemented? */
255 break;
256
257 default:
258 /* probably a 2.0 function */
259 break;
260 }
261
262 local_irq_disable();
263 buf = kmap_atomic(bio_page(rsp->bio), KM_USER0) + bio_offset(rsp->bio);
264 memcpy(buf, resp_data, rsp->data_len);
265 flush_kernel_dcache_page(bio_page(rsp->bio));
266 kunmap_atomic(buf - bio_offset(rsp->bio), KM_USER0);
267 local_irq_enable();
268 rsp->data_len = resp_data_len;
269
270 out:
271 kfree(req_data);
272 kfree(resp_data);
273 return error;
274}
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
index baa0666ffa80..b4f9368f116a 100644
--- a/drivers/scsi/libsas/sas_internal.h
+++ b/drivers/scsi/libsas/sas_internal.h
@@ -80,6 +80,20 @@ struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy);
80 80
81void sas_hae_reset(struct work_struct *work); 81void sas_hae_reset(struct work_struct *work);
82 82
83#ifdef CONFIG_SCSI_SAS_HOST_SMP
84extern int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
85 struct request *rsp);
86#else
87static inline int sas_smp_host_handler(struct Scsi_Host *shost,
88 struct request *req,
89 struct request *rsp)
90{
91 shost_printk(KERN_ERR, shost,
92 "Cannot send SMP to a sas host (not enabled in CONFIG)\n");
93 return -EINVAL;
94}
95#endif
96
83static inline void sas_queue_event(int event, spinlock_t *lock, 97static inline void sas_queue_event(int event, spinlock_t *lock,
84 unsigned long *pending, 98 unsigned long *pending,
85 struct work_struct *work, 99 struct work_struct *work,