• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/scsi/libsas/
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#include <linux/slab.h>
14
15#include "sas_internal.h"
16
17#include <scsi/scsi_transport.h>
18#include <scsi/scsi_transport_sas.h>
19#include "../scsi_sas_internal.h"
20
21static void sas_host_smp_discover(struct sas_ha_struct *sas_ha, u8 *resp_data,
22				  u8 phy_id)
23{
24	struct sas_phy *phy;
25	struct sas_rphy *rphy;
26
27	if (phy_id >= sas_ha->num_phys) {
28		resp_data[2] = SMP_RESP_NO_PHY;
29		return;
30	}
31	resp_data[2] = SMP_RESP_FUNC_ACC;
32
33	phy = sas_ha->sas_phy[phy_id]->phy;
34	resp_data[9] = phy_id;
35	resp_data[13] = phy->negotiated_linkrate;
36	memcpy(resp_data + 16, sas_ha->sas_addr, SAS_ADDR_SIZE);
37	memcpy(resp_data + 24, sas_ha->sas_phy[phy_id]->attached_sas_addr,
38	       SAS_ADDR_SIZE);
39	resp_data[40] = (phy->minimum_linkrate << 4) |
40		phy->minimum_linkrate_hw;
41	resp_data[41] = (phy->maximum_linkrate << 4) |
42		phy->maximum_linkrate_hw;
43
44	if (!sas_ha->sas_phy[phy_id]->port ||
45	    !sas_ha->sas_phy[phy_id]->port->port_dev)
46		return;
47
48	rphy = sas_ha->sas_phy[phy_id]->port->port_dev->rphy;
49	resp_data[12] = rphy->identify.device_type << 4;
50	resp_data[14] = rphy->identify.initiator_port_protocols;
51	resp_data[15] = rphy->identify.target_port_protocols;
52}
53
54static void sas_report_phy_sata(struct sas_ha_struct *sas_ha, u8 *resp_data,
55				u8 phy_id)
56{
57	struct sas_rphy *rphy;
58	struct dev_to_host_fis *fis;
59	int i;
60
61	if (phy_id >= sas_ha->num_phys) {
62		resp_data[2] = SMP_RESP_NO_PHY;
63		return;
64	}
65
66	resp_data[2] = SMP_RESP_PHY_NO_SATA;
67
68	if (!sas_ha->sas_phy[phy_id]->port)
69		return;
70
71	rphy = sas_ha->sas_phy[phy_id]->port->port_dev->rphy;
72	fis = (struct dev_to_host_fis *)
73		sas_ha->sas_phy[phy_id]->port->port_dev->frame_rcvd;
74	if (rphy->identify.target_port_protocols != SAS_PROTOCOL_SATA)
75		return;
76
77	resp_data[2] = SMP_RESP_FUNC_ACC;
78	resp_data[9] = phy_id;
79	memcpy(resp_data + 16, sas_ha->sas_phy[phy_id]->attached_sas_addr,
80	       SAS_ADDR_SIZE);
81
82	/* check to see if we have a valid d2h fis */
83	if (fis->fis_type != 0x34)
84		return;
85
86	/* the d2h fis is required by the standard to be in LE format */
87	for (i = 0; i < 20; i += 4) {
88		u8 *dst = resp_data + 24 + i, *src =
89			&sas_ha->sas_phy[phy_id]->port->port_dev->frame_rcvd[i];
90		dst[0] = src[3];
91		dst[1] = src[2];
92		dst[2] = src[1];
93		dst[3] = src[0];
94	}
95}
96
97static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id,
98			    u8 phy_op, enum sas_linkrate min,
99			    enum sas_linkrate max, u8 *resp_data)
100{
101	struct sas_internal *i =
102		to_sas_internal(sas_ha->core.shost->transportt);
103	struct sas_phy_linkrates rates;
104
105	if (phy_id >= sas_ha->num_phys) {
106		resp_data[2] = SMP_RESP_NO_PHY;
107		return;
108	}
109	switch (phy_op) {
110	case PHY_FUNC_NOP:
111	case PHY_FUNC_LINK_RESET:
112	case PHY_FUNC_HARD_RESET:
113	case PHY_FUNC_DISABLE:
114	case PHY_FUNC_CLEAR_ERROR_LOG:
115	case PHY_FUNC_CLEAR_AFFIL:
116	case PHY_FUNC_TX_SATA_PS_SIGNAL:
117		break;
118
119	default:
120		resp_data[2] = SMP_RESP_PHY_UNK_OP;
121		return;
122	}
123
124	rates.minimum_linkrate = min;
125	rates.maximum_linkrate = max;
126
127	if (i->dft->lldd_control_phy(sas_ha->sas_phy[phy_id], phy_op, &rates))
128		resp_data[2] = SMP_RESP_FUNC_FAILED;
129	else
130		resp_data[2] = SMP_RESP_FUNC_ACC;
131}
132
133int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
134			 struct request *rsp)
135{
136	u8 *req_data = NULL, *resp_data = NULL, *buf;
137	struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
138	int error = -EINVAL;
139
140	/* eight is the minimum size for request and response frames */
141	if (blk_rq_bytes(req) < 8 || blk_rq_bytes(rsp) < 8)
142		goto out;
143
144	if (bio_offset(req->bio) + blk_rq_bytes(req) > PAGE_SIZE ||
145	    bio_offset(rsp->bio) + blk_rq_bytes(rsp) > PAGE_SIZE) {
146		shost_printk(KERN_ERR, shost,
147			"SMP request/response frame crosses page boundary");
148		goto out;
149	}
150
151	req_data = kzalloc(blk_rq_bytes(req), GFP_KERNEL);
152
153	/* make sure frame can always be built ... we copy
154	 * back only the requested length */
155	resp_data = kzalloc(max(blk_rq_bytes(rsp), 128U), GFP_KERNEL);
156
157	if (!req_data || !resp_data) {
158		error = -ENOMEM;
159		goto out;
160	}
161
162	local_irq_disable();
163	buf = kmap_atomic(bio_page(req->bio), KM_USER0) + bio_offset(req->bio);
164	memcpy(req_data, buf, blk_rq_bytes(req));
165	kunmap_atomic(buf - bio_offset(req->bio), KM_USER0);
166	local_irq_enable();
167
168	if (req_data[0] != SMP_REQUEST)
169		goto out;
170
171	/* always succeeds ... even if we can't process the request
172	 * the result is in the response frame */
173	error = 0;
174
175	/* set up default don't know response */
176	resp_data[0] = SMP_RESPONSE;
177	resp_data[1] = req_data[1];
178	resp_data[2] = SMP_RESP_FUNC_UNK;
179
180	switch (req_data[1]) {
181	case SMP_REPORT_GENERAL:
182		req->resid_len -= 8;
183		rsp->resid_len -= 32;
184		resp_data[2] = SMP_RESP_FUNC_ACC;
185		resp_data[9] = sas_ha->num_phys;
186		break;
187
188	case SMP_REPORT_MANUF_INFO:
189		req->resid_len -= 8;
190		rsp->resid_len -= 64;
191		resp_data[2] = SMP_RESP_FUNC_ACC;
192		memcpy(resp_data + 12, shost->hostt->name,
193		       SAS_EXPANDER_VENDOR_ID_LEN);
194		memcpy(resp_data + 20, "libsas virt phy",
195		       SAS_EXPANDER_PRODUCT_ID_LEN);
196		break;
197
198	case SMP_READ_GPIO_REG:
199		break;
200
201	case SMP_DISCOVER:
202		req->resid_len -= 16;
203		if ((int)req->resid_len < 0) {
204			req->resid_len = 0;
205			error = -EINVAL;
206			goto out;
207		}
208		rsp->resid_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		break;
214
215	case SMP_REPORT_PHY_SATA:
216		req->resid_len -= 16;
217		if ((int)req->resid_len < 0) {
218			req->resid_len = 0;
219			error = -EINVAL;
220			goto out;
221		}
222		rsp->resid_len -= 60;
223		sas_report_phy_sata(sas_ha, resp_data, req_data[9]);
224		break;
225
226	case SMP_REPORT_ROUTE_INFO:
227		/* Can't implement; hosts have no routes */
228		break;
229
230	case SMP_WRITE_GPIO_REG:
231		break;
232
233	case SMP_CONF_ROUTE_INFO:
234		/* Can't implement; hosts have no routes */
235		break;
236
237	case SMP_PHY_CONTROL:
238		req->resid_len -= 44;
239		if ((int)req->resid_len < 0) {
240			req->resid_len = 0;
241			error = -EINVAL;
242			goto out;
243		}
244		rsp->resid_len -= 8;
245		sas_phy_control(sas_ha, req_data[9], req_data[10],
246				req_data[32] >> 4, req_data[33] >> 4,
247				resp_data);
248		break;
249
250	case SMP_PHY_TEST_FUNCTION:
251		break;
252
253	default:
254		/* probably a 2.0 function */
255		break;
256	}
257
258	local_irq_disable();
259	buf = kmap_atomic(bio_page(rsp->bio), KM_USER0) + bio_offset(rsp->bio);
260	memcpy(buf, resp_data, blk_rq_bytes(rsp));
261	flush_kernel_dcache_page(bio_page(rsp->bio));
262	kunmap_atomic(buf - bio_offset(rsp->bio), KM_USER0);
263	local_irq_enable();
264
265 out:
266	kfree(req_data);
267	kfree(resp_data);
268	return error;
269}
270