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