1 | /* |
2 | ******************************************************************************* |
3 | ** O.S : Linux |
4 | ** FILE NAME : arcmsr_attr.c |
5 | ** BY : Nick Cheng |
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: support@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.rst |
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 | #include <linux/circ_buf.h> |
54 | |
55 | #include <scsi/scsi_cmnd.h> |
56 | #include <scsi/scsi_device.h> |
57 | #include <scsi/scsi_host.h> |
58 | #include <scsi/scsi_transport.h> |
59 | #include "arcmsr.h" |
60 | |
61 | static ssize_t arcmsr_sysfs_iop_message_read(struct file *filp, |
62 | struct kobject *kobj, |
63 | struct bin_attribute *bin, |
64 | char *buf, loff_t off, |
65 | size_t count) |
66 | { |
67 | struct device *dev = container_of(kobj,struct device,kobj); |
68 | struct Scsi_Host *host = class_to_shost(dev); |
69 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; |
70 | uint8_t *ptmpQbuffer; |
71 | int32_t allxfer_len = 0; |
72 | unsigned long flags; |
73 | |
74 | if (!capable(CAP_SYS_ADMIN)) |
75 | return -EACCES; |
76 | |
77 | /* do message unit read. */ |
78 | ptmpQbuffer = (uint8_t *)buf; |
79 | spin_lock_irqsave(&acb->rqbuffer_lock, flags); |
80 | if (acb->rqbuf_getIndex != acb->rqbuf_putIndex) { |
81 | unsigned int tail = acb->rqbuf_getIndex; |
82 | unsigned int head = acb->rqbuf_putIndex; |
83 | unsigned int cnt_to_end = CIRC_CNT_TO_END(head, tail, ARCMSR_MAX_QBUFFER); |
84 | |
85 | allxfer_len = CIRC_CNT(head, tail, ARCMSR_MAX_QBUFFER); |
86 | if (allxfer_len > ARCMSR_API_DATA_BUFLEN) |
87 | allxfer_len = ARCMSR_API_DATA_BUFLEN; |
88 | |
89 | if (allxfer_len <= cnt_to_end) |
90 | memcpy(ptmpQbuffer, acb->rqbuffer + tail, allxfer_len); |
91 | else { |
92 | memcpy(ptmpQbuffer, acb->rqbuffer + tail, cnt_to_end); |
93 | memcpy(ptmpQbuffer + cnt_to_end, acb->rqbuffer, allxfer_len - cnt_to_end); |
94 | } |
95 | acb->rqbuf_getIndex = (acb->rqbuf_getIndex + allxfer_len) % ARCMSR_MAX_QBUFFER; |
96 | } |
97 | if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { |
98 | struct QBUFFER __iomem *prbuffer; |
99 | acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; |
100 | prbuffer = arcmsr_get_iop_rqbuffer(acb); |
101 | if (arcmsr_Read_iop_rqbuffer_data(acb, prbuffer) == 0) |
102 | acb->acb_flags |= ACB_F_IOPDATA_OVERFLOW; |
103 | } |
104 | spin_unlock_irqrestore(lock: &acb->rqbuffer_lock, flags); |
105 | return allxfer_len; |
106 | } |
107 | |
108 | static ssize_t arcmsr_sysfs_iop_message_write(struct file *filp, |
109 | struct kobject *kobj, |
110 | struct bin_attribute *bin, |
111 | char *buf, loff_t off, |
112 | size_t count) |
113 | { |
114 | struct device *dev = container_of(kobj,struct device,kobj); |
115 | struct Scsi_Host *host = class_to_shost(dev); |
116 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; |
117 | int32_t user_len, cnt2end; |
118 | uint8_t *pQbuffer, *ptmpuserbuffer; |
119 | unsigned long flags; |
120 | |
121 | if (!capable(CAP_SYS_ADMIN)) |
122 | return -EACCES; |
123 | if (count > ARCMSR_API_DATA_BUFLEN) |
124 | return -EINVAL; |
125 | /* do message unit write. */ |
126 | ptmpuserbuffer = (uint8_t *)buf; |
127 | user_len = (int32_t)count; |
128 | spin_lock_irqsave(&acb->wqbuffer_lock, flags); |
129 | if (acb->wqbuf_putIndex != acb->wqbuf_getIndex) { |
130 | arcmsr_write_ioctldata2iop(acb); |
131 | spin_unlock_irqrestore(lock: &acb->wqbuffer_lock, flags); |
132 | return 0; /*need retry*/ |
133 | } else { |
134 | pQbuffer = &acb->wqbuffer[acb->wqbuf_putIndex]; |
135 | cnt2end = ARCMSR_MAX_QBUFFER - acb->wqbuf_putIndex; |
136 | if (user_len > cnt2end) { |
137 | memcpy(pQbuffer, ptmpuserbuffer, cnt2end); |
138 | ptmpuserbuffer += cnt2end; |
139 | user_len -= cnt2end; |
140 | acb->wqbuf_putIndex = 0; |
141 | pQbuffer = acb->wqbuffer; |
142 | } |
143 | memcpy(pQbuffer, ptmpuserbuffer, user_len); |
144 | acb->wqbuf_putIndex += user_len; |
145 | acb->wqbuf_putIndex %= ARCMSR_MAX_QBUFFER; |
146 | if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_CLEARED) { |
147 | acb->acb_flags &= |
148 | ~ACB_F_MESSAGE_WQBUFFER_CLEARED; |
149 | arcmsr_write_ioctldata2iop(acb); |
150 | } |
151 | spin_unlock_irqrestore(lock: &acb->wqbuffer_lock, flags); |
152 | return count; |
153 | } |
154 | } |
155 | |
156 | static ssize_t arcmsr_sysfs_iop_message_clear(struct file *filp, |
157 | struct kobject *kobj, |
158 | struct bin_attribute *bin, |
159 | char *buf, loff_t off, |
160 | size_t count) |
161 | { |
162 | struct device *dev = container_of(kobj,struct device,kobj); |
163 | struct Scsi_Host *host = class_to_shost(dev); |
164 | struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; |
165 | uint8_t *pQbuffer; |
166 | unsigned long flags; |
167 | |
168 | if (!capable(CAP_SYS_ADMIN)) |
169 | return -EACCES; |
170 | |
171 | arcmsr_clear_iop2drv_rqueue_buffer(acb); |
172 | acb->acb_flags |= |
173 | (ACB_F_MESSAGE_WQBUFFER_CLEARED |
174 | | ACB_F_MESSAGE_RQBUFFER_CLEARED |
175 | | ACB_F_MESSAGE_WQBUFFER_READED); |
176 | spin_lock_irqsave(&acb->rqbuffer_lock, flags); |
177 | acb->rqbuf_getIndex = 0; |
178 | acb->rqbuf_putIndex = 0; |
179 | spin_unlock_irqrestore(lock: &acb->rqbuffer_lock, flags); |
180 | spin_lock_irqsave(&acb->wqbuffer_lock, flags); |
181 | acb->wqbuf_getIndex = 0; |
182 | acb->wqbuf_putIndex = 0; |
183 | spin_unlock_irqrestore(lock: &acb->wqbuffer_lock, flags); |
184 | pQbuffer = acb->rqbuffer; |
185 | memset(pQbuffer, 0, sizeof (struct QBUFFER)); |
186 | pQbuffer = acb->wqbuffer; |
187 | memset(pQbuffer, 0, sizeof (struct QBUFFER)); |
188 | return 1; |
189 | } |
190 | |
191 | static const struct bin_attribute arcmsr_sysfs_message_read_attr = { |
192 | .attr = { |
193 | .name = "mu_read" , |
194 | .mode = S_IRUSR , |
195 | }, |
196 | .size = ARCMSR_API_DATA_BUFLEN, |
197 | .read = arcmsr_sysfs_iop_message_read, |
198 | }; |
199 | |
200 | static const struct bin_attribute arcmsr_sysfs_message_write_attr = { |
201 | .attr = { |
202 | .name = "mu_write" , |
203 | .mode = S_IWUSR, |
204 | }, |
205 | .size = ARCMSR_API_DATA_BUFLEN, |
206 | .write = arcmsr_sysfs_iop_message_write, |
207 | }; |
208 | |
209 | static const struct bin_attribute arcmsr_sysfs_message_clear_attr = { |
210 | .attr = { |
211 | .name = "mu_clear" , |
212 | .mode = S_IWUSR, |
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(kobj: &host->shost_dev.kobj, attr: &arcmsr_sysfs_message_read_attr); |
224 | if (error) { |
225 | printk(KERN_ERR "arcmsr: alloc sysfs mu_read failed\n" ); |
226 | goto error_bin_file_message_read; |
227 | } |
228 | error = sysfs_create_bin_file(kobj: &host->shost_dev.kobj, attr: &arcmsr_sysfs_message_write_attr); |
229 | if (error) { |
230 | printk(KERN_ERR "arcmsr: alloc sysfs mu_write failed\n" ); |
231 | goto error_bin_file_message_write; |
232 | } |
233 | error = sysfs_create_bin_file(kobj: &host->shost_dev.kobj, attr: &arcmsr_sysfs_message_clear_attr); |
234 | if (error) { |
235 | printk(KERN_ERR "arcmsr: alloc sysfs mu_clear failed\n" ); |
236 | goto error_bin_file_message_clear; |
237 | } |
238 | return 0; |
239 | error_bin_file_message_clear: |
240 | sysfs_remove_bin_file(kobj: &host->shost_dev.kobj, attr: &arcmsr_sysfs_message_write_attr); |
241 | error_bin_file_message_write: |
242 | sysfs_remove_bin_file(kobj: &host->shost_dev.kobj, attr: &arcmsr_sysfs_message_read_attr); |
243 | error_bin_file_message_read: |
244 | return error; |
245 | } |
246 | |
247 | void arcmsr_free_sysfs_attr(struct AdapterControlBlock *acb) |
248 | { |
249 | struct Scsi_Host *host = acb->host; |
250 | |
251 | sysfs_remove_bin_file(kobj: &host->shost_dev.kobj, attr: &arcmsr_sysfs_message_clear_attr); |
252 | sysfs_remove_bin_file(kobj: &host->shost_dev.kobj, attr: &arcmsr_sysfs_message_write_attr); |
253 | sysfs_remove_bin_file(kobj: &host->shost_dev.kobj, attr: &arcmsr_sysfs_message_read_attr); |
254 | } |
255 | |
256 | |
257 | static ssize_t |
258 | arcmsr_attr_host_driver_version(struct device *dev, |
259 | struct device_attribute *attr, char *buf) |
260 | { |
261 | return snprintf(buf, PAGE_SIZE, |
262 | fmt: "%s\n" , |
263 | ARCMSR_DRIVER_VERSION); |
264 | } |
265 | |
266 | static ssize_t |
267 | arcmsr_attr_host_driver_posted_cmd(struct device *dev, |
268 | struct device_attribute *attr, char *buf) |
269 | { |
270 | struct Scsi_Host *host = class_to_shost(dev); |
271 | struct AdapterControlBlock *acb = |
272 | (struct AdapterControlBlock *) host->hostdata; |
273 | return snprintf(buf, PAGE_SIZE, |
274 | fmt: "%4d\n" , |
275 | atomic_read(v: &acb->ccboutstandingcount)); |
276 | } |
277 | |
278 | static ssize_t |
279 | arcmsr_attr_host_driver_reset(struct device *dev, |
280 | struct device_attribute *attr, char *buf) |
281 | { |
282 | struct Scsi_Host *host = class_to_shost(dev); |
283 | struct AdapterControlBlock *acb = |
284 | (struct AdapterControlBlock *) host->hostdata; |
285 | return snprintf(buf, PAGE_SIZE, |
286 | fmt: "%4d\n" , |
287 | acb->num_resets); |
288 | } |
289 | |
290 | static ssize_t |
291 | arcmsr_attr_host_driver_abort(struct device *dev, |
292 | struct device_attribute *attr, char *buf) |
293 | { |
294 | struct Scsi_Host *host = class_to_shost(dev); |
295 | struct AdapterControlBlock *acb = |
296 | (struct AdapterControlBlock *) host->hostdata; |
297 | return snprintf(buf, PAGE_SIZE, |
298 | fmt: "%4d\n" , |
299 | acb->num_aborts); |
300 | } |
301 | |
302 | static ssize_t |
303 | arcmsr_attr_host_fw_model(struct device *dev, struct device_attribute *attr, |
304 | char *buf) |
305 | { |
306 | struct Scsi_Host *host = class_to_shost(dev); |
307 | struct AdapterControlBlock *acb = |
308 | (struct AdapterControlBlock *) host->hostdata; |
309 | return snprintf(buf, PAGE_SIZE, |
310 | fmt: "%s\n" , |
311 | acb->firm_model); |
312 | } |
313 | |
314 | static ssize_t |
315 | arcmsr_attr_host_fw_version(struct device *dev, |
316 | struct device_attribute *attr, char *buf) |
317 | { |
318 | struct Scsi_Host *host = class_to_shost(dev); |
319 | struct AdapterControlBlock *acb = |
320 | (struct AdapterControlBlock *) host->hostdata; |
321 | |
322 | return snprintf(buf, PAGE_SIZE, |
323 | fmt: "%s\n" , |
324 | acb->firm_version); |
325 | } |
326 | |
327 | static ssize_t |
328 | arcmsr_attr_host_fw_request_len(struct device *dev, |
329 | struct device_attribute *attr, char *buf) |
330 | { |
331 | struct Scsi_Host *host = class_to_shost(dev); |
332 | struct AdapterControlBlock *acb = |
333 | (struct AdapterControlBlock *) host->hostdata; |
334 | |
335 | return snprintf(buf, PAGE_SIZE, |
336 | fmt: "%4d\n" , |
337 | acb->firm_request_len); |
338 | } |
339 | |
340 | static ssize_t |
341 | arcmsr_attr_host_fw_numbers_queue(struct device *dev, |
342 | struct device_attribute *attr, char *buf) |
343 | { |
344 | struct Scsi_Host *host = class_to_shost(dev); |
345 | struct AdapterControlBlock *acb = |
346 | (struct AdapterControlBlock *) host->hostdata; |
347 | |
348 | return snprintf(buf, PAGE_SIZE, |
349 | fmt: "%4d\n" , |
350 | acb->firm_numbers_queue); |
351 | } |
352 | |
353 | static ssize_t |
354 | arcmsr_attr_host_fw_sdram_size(struct device *dev, |
355 | struct device_attribute *attr, char *buf) |
356 | { |
357 | struct Scsi_Host *host = class_to_shost(dev); |
358 | struct AdapterControlBlock *acb = |
359 | (struct AdapterControlBlock *) host->hostdata; |
360 | |
361 | return snprintf(buf, PAGE_SIZE, |
362 | fmt: "%4d\n" , |
363 | acb->firm_sdram_size); |
364 | } |
365 | |
366 | static ssize_t |
367 | arcmsr_attr_host_fw_hd_channels(struct device *dev, |
368 | struct device_attribute *attr, char *buf) |
369 | { |
370 | struct Scsi_Host *host = class_to_shost(dev); |
371 | struct AdapterControlBlock *acb = |
372 | (struct AdapterControlBlock *) host->hostdata; |
373 | |
374 | return snprintf(buf, PAGE_SIZE, |
375 | fmt: "%4d\n" , |
376 | acb->firm_hd_channels); |
377 | } |
378 | |
379 | static DEVICE_ATTR(host_driver_version, S_IRUGO, arcmsr_attr_host_driver_version, NULL); |
380 | static DEVICE_ATTR(host_driver_posted_cmd, S_IRUGO, arcmsr_attr_host_driver_posted_cmd, NULL); |
381 | static DEVICE_ATTR(host_driver_reset, S_IRUGO, arcmsr_attr_host_driver_reset, NULL); |
382 | static DEVICE_ATTR(host_driver_abort, S_IRUGO, arcmsr_attr_host_driver_abort, NULL); |
383 | static DEVICE_ATTR(host_fw_model, S_IRUGO, arcmsr_attr_host_fw_model, NULL); |
384 | static DEVICE_ATTR(host_fw_version, S_IRUGO, arcmsr_attr_host_fw_version, NULL); |
385 | static DEVICE_ATTR(host_fw_request_len, S_IRUGO, arcmsr_attr_host_fw_request_len, NULL); |
386 | static DEVICE_ATTR(host_fw_numbers_queue, S_IRUGO, arcmsr_attr_host_fw_numbers_queue, NULL); |
387 | static DEVICE_ATTR(host_fw_sdram_size, S_IRUGO, arcmsr_attr_host_fw_sdram_size, NULL); |
388 | static DEVICE_ATTR(host_fw_hd_channels, S_IRUGO, arcmsr_attr_host_fw_hd_channels, NULL); |
389 | |
390 | static struct attribute *arcmsr_host_attrs[] = { |
391 | &dev_attr_host_driver_version.attr, |
392 | &dev_attr_host_driver_posted_cmd.attr, |
393 | &dev_attr_host_driver_reset.attr, |
394 | &dev_attr_host_driver_abort.attr, |
395 | &dev_attr_host_fw_model.attr, |
396 | &dev_attr_host_fw_version.attr, |
397 | &dev_attr_host_fw_request_len.attr, |
398 | &dev_attr_host_fw_numbers_queue.attr, |
399 | &dev_attr_host_fw_sdram_size.attr, |
400 | &dev_attr_host_fw_hd_channels.attr, |
401 | NULL, |
402 | }; |
403 | |
404 | static const struct attribute_group arcmsr_host_attr_group = { |
405 | .attrs = arcmsr_host_attrs, |
406 | }; |
407 | |
408 | const struct attribute_group *arcmsr_host_groups[] = { |
409 | &arcmsr_host_attr_group, |
410 | NULL |
411 | }; |
412 | |