diff options
Diffstat (limited to 'drivers/scsi/arcmsr/arcmsr_attr.c')
-rw-r--r-- | drivers/scsi/arcmsr/arcmsr_attr.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/drivers/scsi/arcmsr/arcmsr_attr.c b/drivers/scsi/arcmsr/arcmsr_attr.c new file mode 100644 index 000000000000..12497da5529d --- /dev/null +++ b/drivers/scsi/arcmsr/arcmsr_attr.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /* | ||
2 | ******************************************************************************* | ||
3 | ** O.S : Linux | ||
4 | ** FILE NAME : arcmsr_attr.c | ||
5 | ** BY : Erich Chen | ||
6 | ** Description: attributes exported to sysfs and device host | ||
7 | ******************************************************************************* | ||
8 | ** Copyright (C) 2002 - 2005, Areca Technology Corporation All rights reserved | ||
9 | ** | ||
10 | ** Web site: www.areca.com.tw | ||
11 | ** E-mail: erich@areca.com.tw | ||
12 | ** | ||
13 | ** This program is free software; you can redistribute it and/or modify | ||
14 | ** it under the terms of the GNU General Public License version 2 as | ||
15 | ** published by the Free Software Foundation. | ||
16 | ** This program is distributed in the hope that it will be useful, | ||
17 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | ** GNU General Public License for more details. | ||
20 | ******************************************************************************* | ||
21 | ** Redistribution and use in source and binary forms, with or without | ||
22 | ** modification, are permitted provided that the following conditions | ||
23 | ** are met: | ||
24 | ** 1. Redistributions of source code must retain the above copyright | ||
25 | ** notice, this list of conditions and the following disclaimer. | ||
26 | ** 2. Redistributions in binary form must reproduce the above copyright | ||
27 | ** notice, this list of conditions and the following disclaimer in the | ||
28 | ** documentation and/or other materials provided with the distribution. | ||
29 | ** 3. The name of the author may not be used to endorse or promote products | ||
30 | ** derived from this software without specific prior written permission. | ||
31 | ** | ||
32 | ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
33 | ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
34 | ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
35 | ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
36 | ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING,BUT | ||
37 | ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
38 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY | ||
39 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
40 | ** (INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF | ||
41 | ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
42 | ******************************************************************************* | ||
43 | ** For history of changes, see Documentation/scsi/ChangeLog.arcmsr | ||
44 | ** Firmware Specification, see Documentation/scsi/arcmsr_spec.txt | ||
45 | ******************************************************************************* | ||
46 | */ | ||
47 | #include <linux/module.h> | ||
48 | #include <linux/kernel.h> | ||
49 | #include <linux/init.h> | ||
50 | #include <linux/errno.h> | ||
51 | #include <linux/delay.h> | ||
52 | #include <linux/pci.h> | ||
53 | |||
54 | #include <scsi/scsi_cmnd.h> | ||
55 | #include <scsi/scsi_device.h> | ||
56 | #include <scsi/scsi_host.h> | ||
57 | #include <scsi/scsi_transport.h> | ||
58 | #include "arcmsr.h" | ||
59 | |||
60 | struct class_device_attribute *arcmsr_host_attrs[]; | ||
61 | |||
62 | static ssize_t | ||
63 | arcmsr_sysfs_iop_message_read(struct kobject *kobj, char *buf, loff_t off, | ||
64 | size_t count) | ||
65 | { | ||
66 | struct class_device *cdev = container_of(kobj,struct class_device,kobj); | ||
67 | struct Scsi_Host *host = class_to_shost(cdev); | ||
68 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
69 | struct MessageUnit __iomem *reg = acb->pmu; | ||
70 | uint8_t *pQbuffer,*ptmpQbuffer; | ||
71 | int32_t allxfer_len = 0; | ||
72 | |||
73 | if (!capable(CAP_SYS_ADMIN)) | ||
74 | return -EACCES; | ||
75 | |||
76 | /* do message unit read. */ | ||
77 | ptmpQbuffer = (uint8_t *)buf; | ||
78 | while ((acb->rqbuf_firstindex != acb->rqbuf_lastindex) | ||
79 | && (allxfer_len < 1031)) { | ||
80 | pQbuffer = &acb->rqbuffer[acb->rqbuf_firstindex]; | ||
81 | memcpy(ptmpQbuffer, pQbuffer, 1); | ||
82 | acb->rqbuf_firstindex++; | ||
83 | acb->rqbuf_firstindex %= ARCMSR_MAX_QBUFFER; | ||
84 | ptmpQbuffer++; | ||
85 | allxfer_len++; | ||
86 | } | ||
87 | if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { | ||
88 | struct QBUFFER __iomem * prbuffer = (struct QBUFFER __iomem *) | ||
89 | ®->message_rbuffer; | ||
90 | uint8_t __iomem * iop_data = (uint8_t __iomem *)prbuffer->data; | ||
91 | int32_t iop_len; | ||
92 | |||
93 | acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; | ||
94 | iop_len = readl(&prbuffer->data_len); | ||
95 | while (iop_len > 0) { | ||
96 | acb->rqbuffer[acb->rqbuf_lastindex] = readb(iop_data); | ||
97 | acb->rqbuf_lastindex++; | ||
98 | acb->rqbuf_lastindex %= ARCMSR_MAX_QBUFFER; | ||
99 | iop_data++; | ||
100 | iop_len--; | ||
101 | } | ||
102 | writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, | ||
103 | ®->inbound_doorbell); | ||
104 | } | ||
105 | return (allxfer_len); | ||
106 | } | ||
107 | |||
108 | static ssize_t | ||
109 | arcmsr_sysfs_iop_message_write(struct kobject *kobj, char *buf, loff_t off, | ||
110 | size_t count) | ||
111 | { | ||
112 | struct class_device *cdev = container_of(kobj,struct class_device,kobj); | ||
113 | struct Scsi_Host *host = class_to_shost(cdev); | ||
114 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
115 | int32_t my_empty_len, user_len, wqbuf_firstindex, wqbuf_lastindex; | ||
116 | uint8_t *pQbuffer, *ptmpuserbuffer; | ||
117 | |||
118 | if (!capable(CAP_SYS_ADMIN)) | ||
119 | return -EACCES; | ||
120 | if (count > 1032) | ||
121 | return -EINVAL; | ||
122 | /* do message unit write. */ | ||
123 | ptmpuserbuffer = (uint8_t *)buf; | ||
124 | user_len = (int32_t)count; | ||
125 | wqbuf_lastindex = acb->wqbuf_lastindex; | ||
126 | wqbuf_firstindex = acb->wqbuf_firstindex; | ||
127 | if (wqbuf_lastindex != wqbuf_firstindex) { | ||
128 | arcmsr_post_Qbuffer(acb); | ||
129 | return 0; /*need retry*/ | ||
130 | } else { | ||
131 | my_empty_len = (wqbuf_firstindex-wqbuf_lastindex - 1) | ||
132 | &(ARCMSR_MAX_QBUFFER - 1); | ||
133 | if (my_empty_len >= user_len) { | ||
134 | while (user_len > 0) { | ||
135 | pQbuffer = | ||
136 | &acb->wqbuffer[acb->wqbuf_lastindex]; | ||
137 | memcpy(pQbuffer, ptmpuserbuffer, 1); | ||
138 | acb->wqbuf_lastindex++; | ||
139 | acb->wqbuf_lastindex %= ARCMSR_MAX_QBUFFER; | ||
140 | ptmpuserbuffer++; | ||
141 | user_len--; | ||
142 | } | ||
143 | if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_CLEARED) { | ||
144 | acb->acb_flags &= | ||
145 | ~ACB_F_MESSAGE_WQBUFFER_CLEARED; | ||
146 | arcmsr_post_Qbuffer(acb); | ||
147 | } | ||
148 | return count; | ||
149 | } else { | ||
150 | return 0; /*need retry*/ | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | static ssize_t | ||
156 | arcmsr_sysfs_iop_message_clear(struct kobject *kobj, char *buf, loff_t off, | ||
157 | size_t count) | ||
158 | { | ||
159 | struct class_device *cdev = container_of(kobj,struct class_device,kobj); | ||
160 | struct Scsi_Host *host = class_to_shost(cdev); | ||
161 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
162 | struct MessageUnit __iomem *reg = acb->pmu; | ||
163 | uint8_t *pQbuffer; | ||
164 | |||
165 | if (!capable(CAP_SYS_ADMIN)) | ||
166 | return -EACCES; | ||
167 | |||
168 | if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { | ||
169 | acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; | ||
170 | writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK | ||
171 | , ®->inbound_doorbell); | ||
172 | } | ||
173 | acb->acb_flags |= | ||
174 | (ACB_F_MESSAGE_WQBUFFER_CLEARED | ||
175 | | ACB_F_MESSAGE_RQBUFFER_CLEARED | ||
176 | | ACB_F_MESSAGE_WQBUFFER_READED); | ||
177 | acb->rqbuf_firstindex = 0; | ||
178 | acb->rqbuf_lastindex = 0; | ||
179 | acb->wqbuf_firstindex = 0; | ||
180 | acb->wqbuf_lastindex = 0; | ||
181 | pQbuffer = acb->rqbuffer; | ||
182 | memset(pQbuffer, 0, sizeof (struct QBUFFER)); | ||
183 | pQbuffer = acb->wqbuffer; | ||
184 | memset(pQbuffer, 0, sizeof (struct QBUFFER)); | ||
185 | return 1; | ||
186 | } | ||
187 | |||
188 | static struct bin_attribute arcmsr_sysfs_message_read_attr = { | ||
189 | .attr = { | ||
190 | .name = "mu_read", | ||
191 | .mode = S_IRUSR , | ||
192 | .owner = THIS_MODULE, | ||
193 | }, | ||
194 | .size = 1032, | ||
195 | .read = arcmsr_sysfs_iop_message_read, | ||
196 | }; | ||
197 | |||
198 | static struct bin_attribute arcmsr_sysfs_message_write_attr = { | ||
199 | .attr = { | ||
200 | .name = "mu_write", | ||
201 | .mode = S_IWUSR, | ||
202 | .owner = THIS_MODULE, | ||
203 | }, | ||
204 | .size = 1032, | ||
205 | .write = arcmsr_sysfs_iop_message_write, | ||
206 | }; | ||
207 | |||
208 | static struct bin_attribute arcmsr_sysfs_message_clear_attr = { | ||
209 | .attr = { | ||
210 | .name = "mu_clear", | ||
211 | .mode = S_IWUSR, | ||
212 | .owner = THIS_MODULE, | ||
213 | }, | ||
214 | .size = 1, | ||
215 | .write = arcmsr_sysfs_iop_message_clear, | ||
216 | }; | ||
217 | |||
218 | int arcmsr_alloc_sysfs_attr(struct AdapterControlBlock *acb) | ||
219 | { | ||
220 | struct Scsi_Host *host = acb->host; | ||
221 | int error; | ||
222 | |||
223 | error = sysfs_create_bin_file(&host->shost_classdev.kobj, | ||
224 | &arcmsr_sysfs_message_read_attr); | ||
225 | if (error) { | ||
226 | printk(KERN_ERR "arcmsr: alloc sysfs mu_read failed\n"); | ||
227 | goto error_bin_file_message_read; | ||
228 | } | ||
229 | error = sysfs_create_bin_file(&host->shost_classdev.kobj, | ||
230 | &arcmsr_sysfs_message_write_attr); | ||
231 | if (error) { | ||
232 | printk(KERN_ERR "arcmsr: alloc sysfs mu_write failed\n"); | ||
233 | goto error_bin_file_message_write; | ||
234 | } | ||
235 | error = sysfs_create_bin_file(&host->shost_classdev.kobj, | ||
236 | &arcmsr_sysfs_message_clear_attr); | ||
237 | if (error) { | ||
238 | printk(KERN_ERR "arcmsr: alloc sysfs mu_clear failed\n"); | ||
239 | goto error_bin_file_message_clear; | ||
240 | } | ||
241 | return 0; | ||
242 | error_bin_file_message_clear: | ||
243 | sysfs_remove_bin_file(&host->shost_classdev.kobj, | ||
244 | &arcmsr_sysfs_message_write_attr); | ||
245 | error_bin_file_message_write: | ||
246 | sysfs_remove_bin_file(&host->shost_classdev.kobj, | ||
247 | &arcmsr_sysfs_message_read_attr); | ||
248 | error_bin_file_message_read: | ||
249 | return error; | ||
250 | } | ||
251 | |||
252 | void | ||
253 | arcmsr_free_sysfs_attr(struct AdapterControlBlock *acb) { | ||
254 | struct Scsi_Host *host = acb->host; | ||
255 | |||
256 | sysfs_remove_bin_file(&host->shost_classdev.kobj, | ||
257 | &arcmsr_sysfs_message_clear_attr); | ||
258 | sysfs_remove_bin_file(&host->shost_classdev.kobj, | ||
259 | &arcmsr_sysfs_message_write_attr); | ||
260 | sysfs_remove_bin_file(&host->shost_classdev.kobj, | ||
261 | &arcmsr_sysfs_message_read_attr); | ||
262 | } | ||
263 | |||
264 | |||
265 | static ssize_t | ||
266 | arcmsr_attr_host_driver_version(struct class_device *cdev, char *buf) { | ||
267 | return snprintf(buf, PAGE_SIZE, | ||
268 | "%s\n", | ||
269 | ARCMSR_DRIVER_VERSION); | ||
270 | } | ||
271 | |||
272 | static ssize_t | ||
273 | arcmsr_attr_host_driver_posted_cmd(struct class_device *cdev, char *buf) { | ||
274 | struct Scsi_Host *host = class_to_shost(cdev); | ||
275 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
276 | return snprintf(buf, PAGE_SIZE, | ||
277 | "%4d\n", | ||
278 | atomic_read(&acb->ccboutstandingcount)); | ||
279 | } | ||
280 | |||
281 | static ssize_t | ||
282 | arcmsr_attr_host_driver_reset(struct class_device *cdev, char *buf) { | ||
283 | struct Scsi_Host *host = class_to_shost(cdev); | ||
284 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
285 | return snprintf(buf, PAGE_SIZE, | ||
286 | "%4d\n", | ||
287 | acb->num_resets); | ||
288 | } | ||
289 | |||
290 | static ssize_t | ||
291 | arcmsr_attr_host_driver_abort(struct class_device *cdev, char *buf) { | ||
292 | struct Scsi_Host *host = class_to_shost(cdev); | ||
293 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
294 | return snprintf(buf, PAGE_SIZE, | ||
295 | "%4d\n", | ||
296 | acb->num_aborts); | ||
297 | } | ||
298 | |||
299 | static ssize_t | ||
300 | arcmsr_attr_host_fw_model(struct class_device *cdev, char *buf) { | ||
301 | struct Scsi_Host *host = class_to_shost(cdev); | ||
302 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
303 | return snprintf(buf, PAGE_SIZE, | ||
304 | "%s\n", | ||
305 | acb->firm_model); | ||
306 | } | ||
307 | |||
308 | static ssize_t | ||
309 | arcmsr_attr_host_fw_version(struct class_device *cdev, char *buf) { | ||
310 | struct Scsi_Host *host = class_to_shost(cdev); | ||
311 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
312 | |||
313 | return snprintf(buf, PAGE_SIZE, | ||
314 | "%s\n", | ||
315 | acb->firm_version); | ||
316 | } | ||
317 | |||
318 | static ssize_t | ||
319 | arcmsr_attr_host_fw_request_len(struct class_device *cdev, char *buf) { | ||
320 | struct Scsi_Host *host = class_to_shost(cdev); | ||
321 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
322 | |||
323 | return snprintf(buf, PAGE_SIZE, | ||
324 | "%4d\n", | ||
325 | acb->firm_request_len); | ||
326 | } | ||
327 | |||
328 | static ssize_t | ||
329 | arcmsr_attr_host_fw_numbers_queue(struct class_device *cdev, char *buf) { | ||
330 | struct Scsi_Host *host = class_to_shost(cdev); | ||
331 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
332 | |||
333 | return snprintf(buf, PAGE_SIZE, | ||
334 | "%4d\n", | ||
335 | acb->firm_numbers_queue); | ||
336 | } | ||
337 | |||
338 | static ssize_t | ||
339 | arcmsr_attr_host_fw_sdram_size(struct class_device *cdev, char *buf) { | ||
340 | struct Scsi_Host *host = class_to_shost(cdev); | ||
341 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
342 | |||
343 | return snprintf(buf, PAGE_SIZE, | ||
344 | "%4d\n", | ||
345 | acb->firm_sdram_size); | ||
346 | } | ||
347 | |||
348 | static ssize_t | ||
349 | arcmsr_attr_host_fw_hd_channels(struct class_device *cdev, char *buf) { | ||
350 | struct Scsi_Host *host = class_to_shost(cdev); | ||
351 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
352 | |||
353 | return snprintf(buf, PAGE_SIZE, | ||
354 | "%4d\n", | ||
355 | acb->firm_hd_channels); | ||
356 | } | ||
357 | |||
358 | static CLASS_DEVICE_ATTR(host_driver_version, S_IRUGO, arcmsr_attr_host_driver_version, NULL); | ||
359 | static CLASS_DEVICE_ATTR(host_driver_posted_cmd, S_IRUGO, arcmsr_attr_host_driver_posted_cmd, NULL); | ||
360 | static CLASS_DEVICE_ATTR(host_driver_reset, S_IRUGO, arcmsr_attr_host_driver_reset, NULL); | ||
361 | static CLASS_DEVICE_ATTR(host_driver_abort, S_IRUGO, arcmsr_attr_host_driver_abort, NULL); | ||
362 | static CLASS_DEVICE_ATTR(host_fw_model, S_IRUGO, arcmsr_attr_host_fw_model, NULL); | ||
363 | static CLASS_DEVICE_ATTR(host_fw_version, S_IRUGO, arcmsr_attr_host_fw_version, NULL); | ||
364 | static CLASS_DEVICE_ATTR(host_fw_request_len, S_IRUGO, arcmsr_attr_host_fw_request_len, NULL); | ||
365 | static CLASS_DEVICE_ATTR(host_fw_numbers_queue, S_IRUGO, arcmsr_attr_host_fw_numbers_queue, NULL); | ||
366 | static CLASS_DEVICE_ATTR(host_fw_sdram_size, S_IRUGO, arcmsr_attr_host_fw_sdram_size, NULL); | ||
367 | static CLASS_DEVICE_ATTR(host_fw_hd_channels, S_IRUGO, arcmsr_attr_host_fw_hd_channels, NULL); | ||
368 | |||
369 | struct class_device_attribute *arcmsr_host_attrs[] = { | ||
370 | &class_device_attr_host_driver_version, | ||
371 | &class_device_attr_host_driver_posted_cmd, | ||
372 | &class_device_attr_host_driver_reset, | ||
373 | &class_device_attr_host_driver_abort, | ||
374 | &class_device_attr_host_fw_model, | ||
375 | &class_device_attr_host_fw_version, | ||
376 | &class_device_attr_host_fw_request_len, | ||
377 | &class_device_attr_host_fw_numbers_queue, | ||
378 | &class_device_attr_host_fw_sdram_size, | ||
379 | &class_device_attr_host_fw_hd_channels, | ||
380 | NULL, | ||
381 | }; | ||