qla_attr.c revision 459c537807bd72cce7b007fb218bb5a658a6c3c1
1/*
2 *                  QLOGIC LINUX SOFTWARE
3 *
4 * QLogic ISP2x00 device driver for Linux 2.6.x
5 * Copyright (C) 2003-2005 QLogic Corporation
6 * (www.qlogic.com)
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * General Public License for more details.
17 *
18 */
19#include "qla_def.h"
20
21#include <linux/vmalloc.h>
22#include <scsi/scsi_transport_fc.h>
23
24/* SYSFS attributes --------------------------------------------------------- */
25
26static ssize_t
27qla2x00_sysfs_read_fw_dump(struct kobject *kobj, char *buf, loff_t off,
28    size_t count)
29{
30	struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
31	    struct device, kobj)));
32
33	if (ha->fw_dump_reading == 0)
34		return 0;
35	if (off > ha->fw_dump_buffer_len)
36		return 0;
37	if (off + count > ha->fw_dump_buffer_len)
38		count = ha->fw_dump_buffer_len - off;
39
40	memcpy(buf, &ha->fw_dump_buffer[off], count);
41
42	return (count);
43}
44
45static ssize_t
46qla2x00_sysfs_write_fw_dump(struct kobject *kobj, char *buf, loff_t off,
47    size_t count)
48{
49	struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
50	    struct device, kobj)));
51	int reading;
52	uint32_t dump_size;
53
54	if (off != 0)
55		return (0);
56
57	reading = simple_strtol(buf, NULL, 10);
58	switch (reading) {
59	case 0:
60		if (ha->fw_dump_reading == 1) {
61			qla_printk(KERN_INFO, ha,
62			    "Firmware dump cleared on (%ld).\n",
63			    ha->host_no);
64
65			vfree(ha->fw_dump_buffer);
66			free_pages((unsigned long)ha->fw_dump,
67			    ha->fw_dump_order);
68
69			ha->fw_dump_reading = 0;
70			ha->fw_dump_buffer = NULL;
71			ha->fw_dump = NULL;
72		}
73		break;
74	case 1:
75		if (ha->fw_dump != NULL && !ha->fw_dump_reading) {
76			ha->fw_dump_reading = 1;
77
78			dump_size = FW_DUMP_SIZE_1M;
79			if (ha->fw_memory_size < 0x20000)
80				dump_size = FW_DUMP_SIZE_128K;
81			else if (ha->fw_memory_size < 0x80000)
82				dump_size = FW_DUMP_SIZE_512K;
83			ha->fw_dump_buffer = (char *)vmalloc(dump_size);
84			if (ha->fw_dump_buffer == NULL) {
85				qla_printk(KERN_WARNING, ha,
86				    "Unable to allocate memory for firmware "
87				    "dump buffer (%d).\n", dump_size);
88
89				ha->fw_dump_reading = 0;
90				return (count);
91			}
92			qla_printk(KERN_INFO, ha,
93			    "Firmware dump ready for read on (%ld).\n",
94			    ha->host_no);
95			memset(ha->fw_dump_buffer, 0, dump_size);
96			ha->isp_ops.ascii_fw_dump(ha);
97			ha->fw_dump_buffer_len = strlen(ha->fw_dump_buffer);
98		}
99		break;
100	}
101	return (count);
102}
103
104static struct bin_attribute sysfs_fw_dump_attr = {
105	.attr = {
106		.name = "fw_dump",
107		.mode = S_IRUSR | S_IWUSR,
108		.owner = THIS_MODULE,
109	},
110	.size = 0,
111	.read = qla2x00_sysfs_read_fw_dump,
112	.write = qla2x00_sysfs_write_fw_dump,
113};
114
115static ssize_t
116qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf, loff_t off,
117    size_t count)
118{
119	struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
120	    struct device, kobj)));
121	unsigned long	flags;
122
123	if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->nvram_size)
124		return 0;
125
126	/* Read NVRAM. */
127	spin_lock_irqsave(&ha->hardware_lock, flags);
128	ha->isp_ops.read_nvram(ha, (uint8_t *)buf, ha->nvram_base,
129	    ha->nvram_size);
130	spin_unlock_irqrestore(&ha->hardware_lock, flags);
131
132	return (count);
133}
134
135static ssize_t
136qla2x00_sysfs_write_nvram(struct kobject *kobj, char *buf, loff_t off,
137    size_t count)
138{
139	struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
140	    struct device, kobj)));
141	unsigned long	flags;
142	uint16_t	cnt;
143
144	if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->nvram_size)
145		return 0;
146
147	/* Checksum NVRAM. */
148	if (IS_QLA24XX(ha) || IS_QLA25XX(ha)) {
149		uint32_t *iter;
150		uint32_t chksum;
151
152		iter = (uint32_t *)buf;
153		chksum = 0;
154		for (cnt = 0; cnt < ((count >> 2) - 1); cnt++)
155			chksum += le32_to_cpu(*iter++);
156		chksum = ~chksum + 1;
157		*iter = cpu_to_le32(chksum);
158	} else {
159		uint8_t *iter;
160		uint8_t chksum;
161
162		iter = (uint8_t *)buf;
163		chksum = 0;
164		for (cnt = 0; cnt < count - 1; cnt++)
165			chksum += *iter++;
166		chksum = ~chksum + 1;
167		*iter = chksum;
168	}
169
170	/* Write NVRAM. */
171	spin_lock_irqsave(&ha->hardware_lock, flags);
172	ha->isp_ops.write_nvram(ha, (uint8_t *)buf, ha->nvram_base, count);
173	spin_unlock_irqrestore(&ha->hardware_lock, flags);
174
175	return (count);
176}
177
178static struct bin_attribute sysfs_nvram_attr = {
179	.attr = {
180		.name = "nvram",
181		.mode = S_IRUSR | S_IWUSR,
182		.owner = THIS_MODULE,
183	},
184	.size = 0,
185	.read = qla2x00_sysfs_read_nvram,
186	.write = qla2x00_sysfs_write_nvram,
187};
188
189void
190qla2x00_alloc_sysfs_attr(scsi_qla_host_t *ha)
191{
192	struct Scsi_Host *host = ha->host;
193
194	sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr);
195	sysfs_nvram_attr.size = ha->nvram_size;
196	sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr);
197}
198
199void
200qla2x00_free_sysfs_attr(scsi_qla_host_t *ha)
201{
202	struct Scsi_Host *host = ha->host;
203
204	sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr);
205	sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr);
206}
207
208/* Host attributes. */
209
210static void
211qla2x00_get_host_port_id(struct Scsi_Host *shost)
212{
213	scsi_qla_host_t *ha = to_qla_host(shost);
214
215	fc_host_port_id(shost) = ha->d_id.b.domain << 16 |
216	    ha->d_id.b.area << 8 | ha->d_id.b.al_pa;
217}
218
219static void
220qla2x00_get_starget_node_name(struct scsi_target *starget)
221{
222	struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
223	scsi_qla_host_t *ha = to_qla_host(host);
224	fc_port_t *fcport;
225	uint64_t node_name = 0;
226
227	list_for_each_entry(fcport, &ha->fcports, list) {
228		if (starget->id == fcport->os_target_id) {
229			node_name = *(uint64_t *)fcport->node_name;
230			break;
231		}
232	}
233
234	fc_starget_node_name(starget) = be64_to_cpu(node_name);
235}
236
237static void
238qla2x00_get_starget_port_name(struct scsi_target *starget)
239{
240	struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
241	scsi_qla_host_t *ha = to_qla_host(host);
242	fc_port_t *fcport;
243	uint64_t port_name = 0;
244
245	list_for_each_entry(fcport, &ha->fcports, list) {
246		if (starget->id == fcport->os_target_id) {
247			port_name = *(uint64_t *)fcport->port_name;
248			break;
249		}
250	}
251
252	fc_starget_port_name(starget) = be64_to_cpu(port_name);
253}
254
255static void
256qla2x00_get_starget_port_id(struct scsi_target *starget)
257{
258	struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
259	scsi_qla_host_t *ha = to_qla_host(host);
260	fc_port_t *fcport;
261	uint32_t port_id = ~0U;
262
263	list_for_each_entry(fcport, &ha->fcports, list) {
264		if (starget->id == fcport->os_target_id) {
265			port_id = fcport->d_id.b.domain << 16 |
266			    fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa;
267			break;
268		}
269	}
270
271	fc_starget_port_id(starget) = port_id;
272}
273
274static void
275qla2x00_get_rport_loss_tmo(struct fc_rport *rport)
276{
277	struct Scsi_Host *host = rport_to_shost(rport);
278	scsi_qla_host_t *ha = to_qla_host(host);
279
280	rport->dev_loss_tmo = ha->port_down_retry_count + 5;
281}
282
283static void
284qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
285{
286	struct Scsi_Host *host = rport_to_shost(rport);
287	scsi_qla_host_t *ha = to_qla_host(host);
288
289	if (timeout)
290		ha->port_down_retry_count = timeout;
291	else
292		ha->port_down_retry_count = 1;
293
294	rport->dev_loss_tmo = ha->port_down_retry_count + 5;
295}
296
297struct fc_function_template qla2xxx_transport_functions = {
298
299	.show_host_node_name = 1,
300	.show_host_port_name = 1,
301	.get_host_port_id = qla2x00_get_host_port_id,
302	.show_host_port_id = 1,
303
304	.dd_fcrport_size = sizeof(struct fc_port *),
305
306	.get_starget_node_name = qla2x00_get_starget_node_name,
307	.show_starget_node_name = 1,
308	.get_starget_port_name = qla2x00_get_starget_port_name,
309	.show_starget_port_name = 1,
310	.get_starget_port_id  = qla2x00_get_starget_port_id,
311	.show_starget_port_id = 1,
312
313	.get_rport_dev_loss_tmo = qla2x00_get_rport_loss_tmo,
314	.set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo,
315	.show_rport_dev_loss_tmo = 1,
316
317};
318
319void
320qla2x00_init_host_attr(scsi_qla_host_t *ha)
321{
322	fc_host_node_name(ha->host) =
323	    be64_to_cpu(*(uint64_t *)ha->init_cb->node_name);
324	fc_host_port_name(ha->host) =
325	    be64_to_cpu(*(uint64_t *)ha->init_cb->port_name);
326}
327