diff options
author | Erich Chen <erich@areca.com.tw> | 2006-07-12 11:59:32 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-07-28 15:13:40 -0400 |
commit | 1c57e86d75cf162bdadb3a5fe0cd3f65aa1a9ca3 (patch) | |
tree | 166f691c186d6e663218559c4762299063f63657 /drivers/scsi/arcmsr/arcmsr_attr.c | |
parent | 0c269e6d3c615403a6e0acbe6e88f1c0da9c2396 (diff) |
[SCSI] arcmsr: initial driver, version 1.20.00.13
arcmsr is a driver for the Areca Raid controller, a host based RAID
subsystem that speaks SCSI at the firmware level.
This patch is quite a clean up over the initial submission with
contributions from:
Randy Dunlap <rdunlap@xenotime.net>
Christoph Hellwig <hch@lst.de>
Matthew Wilcox <matthew@wil.cx>
Adrian Bunk <bunk@stusta.de>
Signed-off-by: Erich Chen <erich@areca.com.tw>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/arcmsr/arcmsr_attr.c')
-rw-r--r-- | drivers/scsi/arcmsr/arcmsr_attr.c | 392 |
1 files changed, 392 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..0459f4194d7c --- /dev/null +++ b/drivers/scsi/arcmsr/arcmsr_attr.c | |||
@@ -0,0 +1,392 @@ | |||
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 | error = sysfs_remove_bin_file(&host->shost_classdev.kobj, | ||
244 | &arcmsr_sysfs_message_write_attr); | ||
245 | if (error) | ||
246 | printk(KERN_ERR "arcmsr: sysfs_remove_bin_file mu_write failed\n"); | ||
247 | error_bin_file_message_write: | ||
248 | error = sysfs_remove_bin_file(&host->shost_classdev.kobj, | ||
249 | &arcmsr_sysfs_message_read_attr); | ||
250 | if (error) | ||
251 | printk(KERN_ERR "arcmsr: sysfs_remove_bin_file mu_read failed\n"); | ||
252 | error_bin_file_message_read: | ||
253 | return error; | ||
254 | } | ||
255 | |||
256 | void | ||
257 | arcmsr_free_sysfs_attr(struct AdapterControlBlock *acb) { | ||
258 | struct Scsi_Host *host = acb->host; | ||
259 | int error; | ||
260 | |||
261 | error = sysfs_remove_bin_file(&host->shost_classdev.kobj, | ||
262 | &arcmsr_sysfs_message_clear_attr); | ||
263 | if (error) | ||
264 | printk(KERN_ERR "arcmsr: free sysfs mu_clear failed\n"); | ||
265 | error = sysfs_remove_bin_file(&host->shost_classdev.kobj, | ||
266 | &arcmsr_sysfs_message_write_attr); | ||
267 | if (error) | ||
268 | printk(KERN_ERR "arcmsr: free sysfs mu_write failed\n"); | ||
269 | error = sysfs_remove_bin_file(&host->shost_classdev.kobj, | ||
270 | &arcmsr_sysfs_message_read_attr); | ||
271 | if (error) | ||
272 | printk(KERN_ERR "arcmsr: free sysfss mu_read failed\n"); | ||
273 | } | ||
274 | |||
275 | |||
276 | static ssize_t | ||
277 | arcmsr_attr_host_driver_version(struct class_device *cdev, char *buf) { | ||
278 | return snprintf(buf, PAGE_SIZE, | ||
279 | "ARCMSR: %s\n", | ||
280 | ARCMSR_DRIVER_VERSION); | ||
281 | } | ||
282 | |||
283 | static ssize_t | ||
284 | arcmsr_attr_host_driver_posted_cmd(struct class_device *cdev, char *buf) { | ||
285 | struct Scsi_Host *host = class_to_shost(cdev); | ||
286 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
287 | return snprintf(buf, PAGE_SIZE, | ||
288 | "Current commands posted: %4d\n", | ||
289 | atomic_read(&acb->ccboutstandingcount)); | ||
290 | } | ||
291 | |||
292 | static ssize_t | ||
293 | arcmsr_attr_host_driver_reset(struct class_device *cdev, char *buf) { | ||
294 | struct Scsi_Host *host = class_to_shost(cdev); | ||
295 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
296 | return snprintf(buf, PAGE_SIZE, | ||
297 | "SCSI Host Resets: %4d\n", | ||
298 | acb->num_resets); | ||
299 | } | ||
300 | |||
301 | static ssize_t | ||
302 | arcmsr_attr_host_driver_abort(struct class_device *cdev, char *buf) { | ||
303 | struct Scsi_Host *host = class_to_shost(cdev); | ||
304 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
305 | return snprintf(buf, PAGE_SIZE, | ||
306 | "SCSI Aborts/Timeouts: %4d\n", | ||
307 | acb->num_aborts); | ||
308 | } | ||
309 | |||
310 | static ssize_t | ||
311 | arcmsr_attr_host_fw_model(struct class_device *cdev, char *buf) { | ||
312 | struct Scsi_Host *host = class_to_shost(cdev); | ||
313 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
314 | return snprintf(buf, PAGE_SIZE, | ||
315 | "Adapter Model: %s\n", | ||
316 | acb->firm_model); | ||
317 | } | ||
318 | |||
319 | static ssize_t | ||
320 | arcmsr_attr_host_fw_version(struct class_device *cdev, char *buf) { | ||
321 | struct Scsi_Host *host = class_to_shost(cdev); | ||
322 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
323 | |||
324 | return snprintf(buf, PAGE_SIZE, | ||
325 | "Firmware Version: %s\n", | ||
326 | acb->firm_version); | ||
327 | } | ||
328 | |||
329 | static ssize_t | ||
330 | arcmsr_attr_host_fw_request_len(struct class_device *cdev, char *buf) { | ||
331 | struct Scsi_Host *host = class_to_shost(cdev); | ||
332 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
333 | |||
334 | return snprintf(buf, PAGE_SIZE, | ||
335 | "Reguest Lenth: %4d\n", | ||
336 | acb->firm_request_len); | ||
337 | } | ||
338 | |||
339 | static ssize_t | ||
340 | arcmsr_attr_host_fw_numbers_queue(struct class_device *cdev, char *buf) { | ||
341 | struct Scsi_Host *host = class_to_shost(cdev); | ||
342 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
343 | |||
344 | return snprintf(buf, PAGE_SIZE, | ||
345 | "Numbers of Queue: %4d\n", | ||
346 | acb->firm_numbers_queue); | ||
347 | } | ||
348 | |||
349 | static ssize_t | ||
350 | arcmsr_attr_host_fw_sdram_size(struct class_device *cdev, char *buf) { | ||
351 | struct Scsi_Host *host = class_to_shost(cdev); | ||
352 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
353 | |||
354 | return snprintf(buf, PAGE_SIZE, | ||
355 | "SDRAM Size: %4d\n", | ||
356 | acb->firm_sdram_size); | ||
357 | } | ||
358 | |||
359 | static ssize_t | ||
360 | arcmsr_attr_host_fw_hd_channels(struct class_device *cdev, char *buf) { | ||
361 | struct Scsi_Host *host = class_to_shost(cdev); | ||
362 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; | ||
363 | |||
364 | return snprintf(buf, PAGE_SIZE, | ||
365 | "Hard Disk Channels: %4d\n", | ||
366 | acb->firm_hd_channels); | ||
367 | } | ||
368 | |||
369 | static CLASS_DEVICE_ATTR(host_driver_version, S_IRUGO, arcmsr_attr_host_driver_version, NULL); | ||
370 | static CLASS_DEVICE_ATTR(host_driver_posted_cmd, S_IRUGO, arcmsr_attr_host_driver_posted_cmd, NULL); | ||
371 | static CLASS_DEVICE_ATTR(host_driver_reset, S_IRUGO, arcmsr_attr_host_driver_reset, NULL); | ||
372 | static CLASS_DEVICE_ATTR(host_driver_abort, S_IRUGO, arcmsr_attr_host_driver_abort, NULL); | ||
373 | static CLASS_DEVICE_ATTR(host_fw_model, S_IRUGO, arcmsr_attr_host_fw_model, NULL); | ||
374 | static CLASS_DEVICE_ATTR(host_fw_version, S_IRUGO, arcmsr_attr_host_fw_version, NULL); | ||
375 | static CLASS_DEVICE_ATTR(host_fw_request_len, S_IRUGO, arcmsr_attr_host_fw_request_len, NULL); | ||
376 | static CLASS_DEVICE_ATTR(host_fw_numbers_queue, S_IRUGO, arcmsr_attr_host_fw_numbers_queue, NULL); | ||
377 | static CLASS_DEVICE_ATTR(host_fw_sdram_size, S_IRUGO, arcmsr_attr_host_fw_sdram_size, NULL); | ||
378 | static CLASS_DEVICE_ATTR(host_fw_hd_channels, S_IRUGO, arcmsr_attr_host_fw_hd_channels, NULL); | ||
379 | |||
380 | struct class_device_attribute *arcmsr_host_attrs[] = { | ||
381 | &class_device_attr_host_driver_version, | ||
382 | &class_device_attr_host_driver_posted_cmd, | ||
383 | &class_device_attr_host_driver_reset, | ||
384 | &class_device_attr_host_driver_abort, | ||
385 | &class_device_attr_host_fw_model, | ||
386 | &class_device_attr_host_fw_version, | ||
387 | &class_device_attr_host_fw_request_len, | ||
388 | &class_device_attr_host_fw_numbers_queue, | ||
389 | &class_device_attr_host_fw_sdram_size, | ||
390 | &class_device_attr_host_fw_hd_channels, | ||
391 | NULL, | ||
392 | }; | ||