File: /usr/src/linux/drivers/message/fusion/mptctl.c
1 /*
2 * linux/drivers/message/fusion/mptctl.c
3 * Fusion MPT misc device (ioctl) driver.
4 * For use with PCI chip/adapter(s):
5 * LSIFC9xx/LSI409xx Fibre Channel
6 * running LSI Logic Fusion MPT (Message Passing Technology) firmware.
7 *
8 * Credits:
9 * This driver would not exist if not for Alan Cox's development
10 * of the linux i2o driver.
11 *
12 * A huge debt of gratitude is owed to David S. Miller (DaveM)
13 * for fixing much of the stupid and broken stuff in the early
14 * driver while porting to sparc64 platform. THANK YOU!
15 *
16 * A big THANKS to Eddie C. Dost for fixing the ioctl path
17 * and most importantly f/w download on sparc64 platform!
18 * (plus Eddie's other helpful hints and insights)
19 *
20 * Thanks to Arnaldo Carvalho de Melo for finding and patching
21 * a potential memory leak in mpt_ioctl_do_fw_download(),
22 * and for some kmalloc insight:-)
23 *
24 * (see also mptbase.c)
25 *
26 * Copyright (c) 1999-2001 LSI Logic Corporation
27 * Originally By: Steven J. Ralston, Noah Romer
28 * (mailto:Steve.Ralston@lsil.com)
29 *
30 * $Id: mptctl.c,v 1.25.4.1 2001/08/24 20:07:06 sralston Exp $
31 */
32 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
33 /*
34 This program is free software; you can redistribute it and/or modify
35 it under the terms of the GNU General Public License as published by
36 the Free Software Foundation; version 2 of the License.
37
38 This program is distributed in the hope that it will be useful,
39 but WITHOUT ANY WARRANTY; without even the implied warranty of
40 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41 GNU General Public License for more details.
42
43 NO WARRANTY
44 THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
45 CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
46 LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
47 MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
48 solely responsible for determining the appropriateness of using and
49 distributing the Program and assumes all risks associated with its
50 exercise of rights under this Agreement, including but not limited to
51 the risks and costs of program errors, damage to or loss of data,
52 programs or equipment, and unavailability or interruption of operations.
53
54 DISCLAIMER OF LIABILITY
55 NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
56 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
58 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
59 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
60 USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
61 HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
62
63 You should have received a copy of the GNU General Public License
64 along with this program; if not, write to the Free Software
65 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
66 */
67 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
68
69 #include <linux/version.h>
70 #include <linux/kernel.h>
71 #include <linux/module.h>
72 #include <linux/errno.h>
73 #include <linux/init.h>
74 #include <linux/slab.h>
75 #include <linux/types.h>
76 #include <linux/pci.h>
77 #include <linux/miscdevice.h>
78
79 #include <asm/io.h>
80 #include <asm/uaccess.h>
81
82 #include <linux/proc_fs.h>
83
84 #define COPYRIGHT "Copyright (c) 1999-2001 LSI Logic Corporation"
85 #define MODULEAUTHOR "Steven J. Ralston, Noah Romer"
86 #include "mptbase.h"
87
88 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
89 #define my_NAME "Fusion MPT misc device (ioctl) driver"
90 #define my_VERSION MPT_LINUX_VERSION_COMMON
91 #define MYNAM "mptctl"
92
93 EXPORT_NO_SYMBOLS;
94 MODULE_AUTHOR(MODULEAUTHOR);
95 MODULE_DESCRIPTION(my_NAME);
96
97 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
98
99 static int mptctl_id = -1;
100 static int rwperf_reset = 0;
101 static struct semaphore mptctl_syscall_sem_ioc[MPT_MAX_ADAPTERS];
102
103 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
104
105 static int mpt_ioctl_rwperf(unsigned long arg);
106 static int mpt_ioctl_rwperf_status(unsigned long arg);
107 static int mpt_ioctl_rwperf_reset(unsigned long arg);
108 static int mpt_ioctl_fw_download(unsigned long arg);
109 static int mpt_ioctl_do_fw_download(int ioc, char *ufwbuf, size_t fwlen);
110 static int mpt_ioctl_scsi_cmd(unsigned long arg);
111
112 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
113 /*
114 * Scatter gather list (SGL) sizes and limits...
115 */
116 //#define MAX_SCSI_FRAGS 9
117 #define MAX_FRAGS_SPILL1 9
118 #define MAX_FRAGS_SPILL2 15
119 #define FRAGS_PER_BUCKET (MAX_FRAGS_SPILL2 + 1)
120
121 //#define MAX_CHAIN_FRAGS 64
122 //#define MAX_CHAIN_FRAGS (15+15+15+16)
123 #define MAX_CHAIN_FRAGS (4 * MAX_FRAGS_SPILL2 + 1)
124
125 // Define max sg LIST bytes ( == (#frags + #chains) * 8 bytes each)
126 // Works out to: 592d bytes! (9+1)*8 + 4*(15+1)*8
127 // ^----------------- 80 + 512
128 #define MAX_SGL_BYTES ((MAX_FRAGS_SPILL1 + 1 + (4 * FRAGS_PER_BUCKET)) * 8)
129
130 /* linux only seems to ever give 128kB MAX contiguous (GFP_USER) mem bytes */
131 #define MAX_KMALLOC_SZ (128*1024)
132
133 struct buflist {
134 u8 *kptr;
135 int len;
136 };
137
138 #define myMAX_TARGETS (1<<4)
139 #define myMAX_LUNS (1<<3)
140 #define myMAX_T_MASK (myMAX_TARGETS-1)
141 #define myMAX_L_MASK (myMAX_LUNS-1)
142 static u8 DevInUse[myMAX_TARGETS][myMAX_LUNS] = {{0,0}};
143 static u32 DevIosCount[myMAX_TARGETS][myMAX_LUNS] = {{0,0}};
144
145 static u32 fwReplyBuffer[16];
146 static pMPIDefaultReply_t ReplyMsg = NULL;
147
148 /* some private forw protos */
149 static SGESimple32_t *kbuf_alloc_2_sgl( int bytes, u32 dir, int *frags,
150 struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc);
151 static void kfree_sgl( SGESimple32_t *sgl, dma_addr_t sgl_dma,
152 struct buflist *buflist, MPT_ADAPTER *ioc);
153
154 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
155 /**
156 * mptctl_syscall_down - Down the MPT adapter syscall semaphore.
157 * @ioc: Pointer to MPT adapter
158 * @nonblock: boolean, non-zero if O_NONBLOCK is set
159 *
160 * All of the mptctl commands can potentially sleep, which is illegal
161 * with a spinlock held, thus we perform mutual exclusion here.
162 *
163 * Returns negative errno on error, or zero for success.
164 */
165 static inline int
166 mptctl_syscall_down(MPT_ADAPTER *ioc, int nonblock)
167 {
168 dprintk((KERN_INFO MYNAM "::mpt_syscall_down(%p,%d) called\n", ioc, nonblock));
169
170 if (nonblock) {
171 if (down_trylock(&mptctl_syscall_sem_ioc[ioc->id]))
172 return -EAGAIN;
173 } else {
174 if (down_interruptible(&mptctl_syscall_sem_ioc[ioc->id]))
175 return -ERESTARTSYS;
176 }
177 return 0;
178 }
179
180 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
181 /*
182 * This is the callback for any message we have posted. The message itself
183 * will be returned to the message pool when we return from the IRQ
184 *
185 * This runs in irq context so be short and sweet.
186 */
187 static int
188 mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply)
189 {
190 u8 targ;
191
192 //dprintk((KERN_DEBUG MYNAM ": Got mptctl_reply()!\n"));
193
194 if (req && req->u.hdr.Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
195 targ = req->u.scsireq.TargetID & myMAX_T_MASK;
196 DevIosCount[targ][0]--;
197 } else if (reply && req && req->u.hdr.Function == MPI_FUNCTION_FW_DOWNLOAD) {
198 // NOTE: Expects/requires non-Turbo reply!
199 dprintk((KERN_INFO MYNAM ": Caching MPI_FUNCTION_FW_DOWNLOAD reply!\n"));
200 memcpy(fwReplyBuffer, reply, MIN(sizeof(fwReplyBuffer), 4*reply->u.reply.MsgLength));
201 ReplyMsg = (pMPIDefaultReply_t) fwReplyBuffer;
202 }
203
204 return 1;
205 }
206
207 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
208 /*
209 * struct file_operations functionality.
210 * Members:
211 * llseek, write, read, ioctl, open, release
212 */
213 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
214 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9)
215 static loff_t
216 mptctl_llseek(struct file *file, loff_t offset, int origin)
217 {
218 return -ESPIPE;
219 }
220 #define no_llseek mptctl_llseek
221 #endif
222
223 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
224 static ssize_t
225 mptctl_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
226 {
227 printk(KERN_ERR MYNAM ": ioctl WRITE not yet supported\n");
228 return 0;
229 }
230
231 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
232 static ssize_t
233 mptctl_read(struct file *file, char *buf, size_t count, loff_t *ptr)
234 {
235 return 0;
236 }
237
238 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
239 /*
240 * MPT ioctl handler
241 */
242 static int
243 mpt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
244 {
245 struct mpt_ioctl_sanity *usanity = (struct mpt_ioctl_sanity *) arg;
246 struct mpt_ioctl_sanity ksanity;
247 int iocnum;
248 unsigned iocnumX;
249 int nonblock = (file->f_flags & O_NONBLOCK);
250 int ret;
251 MPT_ADAPTER *iocp = NULL;
252
253 dprintk((KERN_INFO MYNAM "::mpt_ioctl() called\n"));
254
255 if (copy_from_user(&ksanity, usanity, sizeof(ksanity))) {
256 printk(KERN_ERR "%s::mpt_ioctl() @%d - "
257 "Unable to copy mpt_ioctl_sanity data @ %p\n",
258 __FILE__, __LINE__, (void*)usanity);
259 return -EFAULT;
260 }
261 ret = -ENXIO; /* (-6) No such device or address */
262
263 /* Verify intended MPT adapter */
264 iocnumX = ksanity.iocnum & 0xFF;
265 if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) ||
266 (iocp == NULL)) {
267 printk(KERN_ERR "%s::mpt_ioctl() @%d - ioc%d not found!\n",
268 __FILE__, __LINE__, iocnumX);
269 return -ENODEV;
270 }
271
272 if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0)
273 return ret;
274
275 dprintk((KERN_INFO MYNAM "::mpt_ioctl() - Using %s\n", iocp->name));
276
277 switch(cmd) {
278 case MPTRWPERF:
279 ret = mpt_ioctl_rwperf(arg);
280 break;
281 case MPTRWPERF_CHK:
282 ret = mpt_ioctl_rwperf_status(arg);
283 break;
284 case MPTRWPERF_RESET:
285 ret = mpt_ioctl_rwperf_reset(arg);
286 break;
287 case MPTFWDOWNLOAD:
288 ret = mpt_ioctl_fw_download(arg);
289 break;
290 case MPTSCSICMD:
291 ret = mpt_ioctl_scsi_cmd(arg);
292 break;
293 default:
294 ret = -EINVAL;
295 }
296
297 up(&mptctl_syscall_sem_ioc[iocp->id]);
298
299 return ret;
300 }
301
302 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
303 static int mptctl_open(struct inode *inode, struct file *file)
304 {
305 /*
306 * Should support multiple management users
307 */
308 return 0;
309 }
310
311 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
312 static int mptctl_release(struct inode *inode, struct file *file)
313 {
314 return 0;
315 }
316
317 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
318 static int
319 mpt_ioctl_fw_download(unsigned long arg)
320 {
321 struct mpt_fw_xfer *ufwdl = (struct mpt_fw_xfer *) arg;
322 struct mpt_fw_xfer kfwdl;
323
324 dprintk((KERN_INFO "mpt_ioctl_fwdl called. mptctl_id = %xh\n", mptctl_id)); //tc
325 if (copy_from_user(&kfwdl, ufwdl, sizeof(struct mpt_fw_xfer))) {
326 printk(KERN_ERR "%s@%d::_ioctl_fwdl - "
327 "Unable to copy mpt_fw_xfer struct @ %p\n",
328 __FILE__, __LINE__, (void*)ufwdl);
329 return -EFAULT;
330 }
331
332 return mpt_ioctl_do_fw_download(kfwdl.iocnum, kfwdl.bufp, kfwdl.fwlen);
333 }
334
335 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
336 /*
337 * MPT FW Download
338 */
339 static int
340 mpt_ioctl_do_fw_download(int ioc, char *ufwbuf, size_t fwlen)
341 {
342 FWDownload_t *dlmsg;
343 MPT_FRAME_HDR *mf;
344 MPT_ADAPTER *iocp;
345 // char *fwbuf;
346 // dma_addr_t fwbuf_dma;
347 FWDownloadTCSGE_t *fwVoodoo;
348 // SGEAllUnion_t *fwSgl;
349 int ret;
350
351 SGESimple32_t *sgl;
352 SGESimple32_t *sgOut, *sgIn;
353 dma_addr_t sgl_dma;
354 struct buflist *buflist = NULL;
355 struct buflist *bl = NULL;
356 int numfrags = 0;
357 int maxfrags;
358 int n = 0;
359 u32 sgdir;
360 u32 nib;
361 int fw_bytes_copied = 0;
362 u16 iocstat;
363 int i;
364
365 dprintk((KERN_INFO "mpt_ioctl_do_fwdl called. mptctl_id = %xh.\n", mptctl_id));
366
367 dprintk((KERN_INFO "DbG: kfwdl.bufp = %p\n", ufwbuf));
368 dprintk((KERN_INFO "DbG: kfwdl.fwlen = %d\n", (int)fwlen));
369 dprintk((KERN_INFO "DbG: kfwdl.ioc = %04xh\n", ioc));
370
371 if ((ioc = mpt_verify_adapter(ioc, &iocp)) < 0) {
372 printk("%s@%d::_ioctl_fwdl - ioc%d not found!\n",
373 __FILE__, __LINE__, ioc);
374 return -ENXIO; /* (-6) No such device or address */
375 }
376
377 if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL)
378 return -EAGAIN;
379 dlmsg = (FWDownload_t*) mf;
380 fwVoodoo = (FWDownloadTCSGE_t *) &dlmsg->SGL;
381 sgOut = (SGESimple32_t *) (fwVoodoo + 1);
382
383 /*
384 * Construct f/w download request
385 */
386 dlmsg->ImageType = MPI_FW_DOWNLOAD_ITYPE_FW;
387 dlmsg->Reserved = 0;
388 dlmsg->ChainOffset = 0;
389 dlmsg->Function = MPI_FUNCTION_FW_DOWNLOAD;
390 dlmsg->Reserved1[0] = dlmsg->Reserved1[1] = dlmsg->Reserved1[2] = 0;
391 dlmsg->MsgFlags = 0;
392
393 fwVoodoo->Reserved = 0;
394 fwVoodoo->ContextSize = 0;
395 fwVoodoo->DetailsLength = 12;
396 fwVoodoo->Flags = MPI_SGE_FLAGS_TRANSACTION_ELEMENT;
397 fwVoodoo->Reserved1 = 0;
398 fwVoodoo->ImageOffset = 0;
399 fwVoodoo->ImageSize = cpu_to_le32(fwlen);
400
401 /*
402 * Need to kmalloc area(s) for holding firmware image bytes.
403 * But we need to do it piece meal, using a proper
404 * scatter gather list (with 128kB MAX hunks).
405 *
406 * A practical limit here might be # of sg hunks that fit into
407 * a single IOC request frame; 12 or 8 (see below), so:
408 * For FC9xx: 12 x 128kB == 1.5 mB (max)
409 * For C1030: 8 x 128kB == 1 mB (max)
410 * We could support chaining, but things get ugly(ier:)
411 */
412 sgdir = 0x04000000; /* IOC will READ from sys mem */
413 if ((sgl = kbuf_alloc_2_sgl(fwlen, sgdir, &numfrags, &buflist, &sgl_dma, iocp)) == NULL)
414 return -ENOMEM;
415
416 /*
417 * We should only need SGL with 2 simple_32bit entries (up to 256 kB)
418 * for FC9xx f/w image, but calculate max number of sge hunks
419 * we can fit into a request frame, and limit ourselves to that.
420 * (currently no chain support)
421 * For FC9xx: (128-12-16)/8 = 12.5 = 12
422 * For C1030: (96-12-16)/8 = 8.5 = 8
423 */
424 maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - sizeof(FWDownloadTCSGE_t)) / sizeof(SGESimple32_t);
425 if (numfrags > maxfrags) {
426 ret = -EMLINK;
427 goto fwdl_out;
428 }
429
430 dprintk((KERN_INFO "DbG: sgl buffer = %p, sgfrags = %d\n", sgl, numfrags));
431
432 /*
433 * Parse SG list, copying sgl itself,
434 * plus f/w image hunks from user space as we go...
435 */
436 ret = -EFAULT;
437 sgIn = sgl;
438 bl = buflist;
439 for (i=0; i < numfrags; i++) {
440 nib = (le32_to_cpu(sgIn->FlagsLength) & 0xF0000000) >> 28;
441 /* skip ignore/chain. */
442 if (nib == 0 || nib == 3) {
443 ;
444 } else if (sgIn->Address) {
445 *sgOut = *sgIn;
446 n++;
447 if (copy_from_user(bl->kptr, ufwbuf+fw_bytes_copied, bl->len)) {
448 printk(KERN_ERR "%s@%d::_ioctl_fwdl - "
449 "Unable to copy f/w buffer hunk#%d @ %p\n",
450 __FILE__, __LINE__, n, (void*)ufwbuf);
451 goto fwdl_out;
452 }
453 fw_bytes_copied += bl->len;
454 }
455 sgIn++;
456 bl++;
457 sgOut++;
458 }
459
460 #ifdef MPT_DEBUG
461 {
462 u32 *m = (u32 *)mf;
463 printk(KERN_INFO MYNAM ": F/W download request:\n" KERN_INFO " ");
464 for (i=0; i < 7+numfrags*2; i++)
465 printk(" %08x", le32_to_cpu(m[i]));
466 printk("\n");
467 }
468 #endif
469
470 /*
471 * Finally, perform firmware download.
472 */
473 ReplyMsg = NULL;
474 mpt_put_msg_frame(mptctl_id, ioc, mf);
475
476 /*
477 * Wait until the reply has been received
478 */
479 {
480 int foo = 0;
481
482 while (ReplyMsg == NULL) {
483 if (!(foo%1000000)) {
484 dprintk((KERN_INFO "DbG::_do_fwdl: "
485 "In ReplyMsg loop - iteration %d\n",
486 foo)); //tc
487 }
488 ret = -ETIME;
489 if (++foo > 60000000)
490 goto fwdl_out;
491 mb();
492 schedule();
493 barrier();
494 }
495 }
496
497 if (sgl)
498 kfree_sgl(sgl, sgl_dma, buflist, iocp);
499
500 iocstat = le16_to_cpu(ReplyMsg->IOCStatus) & MPI_IOCSTATUS_MASK;
501 if (iocstat == MPI_IOCSTATUS_SUCCESS) {
502 printk(KERN_INFO MYNAM ": F/W update successfully sent to %s!\n", iocp->name);
503 return 0;
504 } else if (iocstat == MPI_IOCSTATUS_INVALID_FUNCTION) {
505 printk(KERN_WARNING MYNAM ": ?Hmmm... %s says it doesn't support F/W download!?!\n",
506 iocp->name);
507 printk(KERN_WARNING MYNAM ": (time to go bang on somebodies door)\n");
508 return -EBADRQC;
509 } else if (iocstat == MPI_IOCSTATUS_BUSY) {
510 printk(KERN_WARNING MYNAM ": Warning! %s says: IOC_BUSY!\n", iocp->name);
511 printk(KERN_WARNING MYNAM ": (try again later?)\n");
512 return -EBUSY;
513 } else {
514 printk(KERN_WARNING MYNAM "::ioctl_fwdl() ERROR! %s returned [bad] status = %04xh\n",
515 iocp->name, iocstat);
516 printk(KERN_WARNING MYNAM ": (bad VooDoo)\n");
517 return -ENOMSG;
518 }
519 return 0;
520
521 fwdl_out:
522 kfree_sgl(sgl, sgl_dma, buflist, iocp);
523 return ret;
524 }
525
526 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
527 /*
528 * NEW rwperf (read/write performance) stuff starts here...
529 */
530
531 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
532 static SGESimple32_t *
533 kbuf_alloc_2_sgl(int bytes, u32 sgdir, int *frags,
534 struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc)
535 {
536 SGESimple32_t *sglbuf = NULL;
537 struct buflist *buflist = NULL;
538 int numfrags = 0;
539 int fragcnt = 0;
540 int alloc_sz = MIN(bytes,MAX_KMALLOC_SZ); // avoid kernel warning msg!
541 int bytes_allocd = 0;
542 int this_alloc;
543 SGESimple32_t *sgl;
544 u32 pa; // phys addr
545 SGEChain32_t *last_chain = NULL;
546 SGEChain32_t *old_chain = NULL;
547 int chaincnt = 0;
548 int i, buflist_ent;
549 int sg_spill = MAX_FRAGS_SPILL1;
550 int dir;
551
552 *frags = 0;
553 *blp = NULL;
554 i = MAX_SGL_BYTES / 8;
555 buflist = kmalloc(i, GFP_USER);
556 if (buflist == NULL)
557 return NULL;
558 memset(buflist, 0, i);
559 buflist_ent = 0;
560
561 sglbuf = pci_alloc_consistent(ioc->pcidev, MAX_SGL_BYTES, sglbuf_dma);
562 if (sglbuf == NULL)
563 goto free_and_fail;
564
565 if (sgdir & 0x04000000)
566 dir = PCI_DMA_TODEVICE;
567 else
568 dir = PCI_DMA_FROMDEVICE;
569
570 sgl = sglbuf;
571 while (bytes_allocd < bytes) {
572 this_alloc = MIN(alloc_sz, bytes-bytes_allocd);
573 buflist[buflist_ent].len = this_alloc;
574 buflist[buflist_ent].kptr = pci_alloc_consistent(ioc->pcidev,
575 this_alloc,
576 &pa);
577 if (buflist[buflist_ent].kptr == NULL) {
578 alloc_sz = alloc_sz / 2;
579 if (alloc_sz == 0) {
580 printk(KERN_WARNING MYNAM "-SG: No can do - "
581 "not enough memory! :-(\n");
582 printk(KERN_WARNING MYNAM "-SG: (freeing %d frags)\n",
583 numfrags);
584 goto free_and_fail;
585 }
586 continue;
587 } else {
588 dma_addr_t dma_addr;
589
590 bytes_allocd += this_alloc;
591
592 /* Write one SIMPLE sge */
593 sgl->FlagsLength = cpu_to_le32(0x10000000|sgdir|this_alloc);
594 dma_addr = pci_map_single(ioc->pcidev, buflist[buflist_ent].kptr, this_alloc, dir);
595 sgl->Address = cpu_to_le32(dma_addr);
596
597 fragcnt++;
598 numfrags++;
599 sgl++;
600 buflist_ent++;
601 }
602
603 if (bytes_allocd >= bytes)
604 break;
605
606 /* Need to chain? */
607 if (fragcnt == sg_spill) {
608 dma_addr_t chain_link;
609
610 if (last_chain != NULL)
611 last_chain->NextChainOffset = 0x1E;
612
613 fragcnt = 0;
614 sg_spill = MAX_FRAGS_SPILL2;
615
616 /* fixup previous SIMPLE sge */
617 sgl[-1].FlagsLength |= cpu_to_le32(0x80000000);
618
619 chain_link = (*sglbuf_dma) +
620 ((u8 *)(sgl+1) - (u8 *)sglbuf);
621
622 /* Write one CHAIN sge */
623 sgl->FlagsLength = cpu_to_le32(0x30000080);
624 sgl->Address = cpu_to_le32(chain_link);
625
626 old_chain = last_chain;
627 last_chain = (SGEChain32_t*)sgl;
628 chaincnt++;
629 numfrags++;
630 sgl++;
631 }
632
633 /* overflow check... */
634 if (numfrags*8 > MAX_SGL_BYTES) {
635 /* GRRRRR... */
636 printk(KERN_WARNING MYNAM "-SG: No can do - "
637 "too many SG frags! :-(\n");
638 printk(KERN_WARNING MYNAM "-SG: (freeing %d frags)\n",
639 numfrags);
640 goto free_and_fail;
641 }
642 }
643
644 /* Last sge fixup: set LE+eol+eob bits */
645 sgl[-1].FlagsLength |= cpu_to_le32(0xC1000000);
646
647 /* Chain fixup needed? */
648 if (last_chain != NULL && fragcnt < 16)
649 last_chain->Length = cpu_to_le16(fragcnt * 8);
650
651 *frags = numfrags;
652 *blp = buflist;
653
654 dprintk((KERN_INFO MYNAM "-SG: kbuf_alloc_2_sgl() - "
655 "%d SG frags generated! (%d CHAIN%s)\n",
656 numfrags, chaincnt, chaincnt>1?"s":""));
657
658 dprintk((KERN_INFO MYNAM "-SG: kbuf_alloc_2_sgl() - "
659 "last (big) alloc_sz=%d\n",
660 alloc_sz));
661
662 return sglbuf;
663
664 free_and_fail:
665 if (sglbuf != NULL) {
666 int i;
667
668 for (i = 0; i < numfrags; i++) {
669 dma_addr_t dma_addr;
670 u8 *kptr;
671 int len;
672
673 if ((le32_to_cpu(sglbuf[i].FlagsLength) >> 24) == 0x30)
674 continue;
675
676 dma_addr = le32_to_cpu(sglbuf[i].Address);
677 kptr = buflist[i].kptr;
678 len = buflist[i].len;
679
680 pci_free_consistent(ioc->pcidev, len, kptr, dma_addr);
681 }
682 pci_free_consistent(ioc->pcidev, MAX_SGL_BYTES, sglbuf, *sglbuf_dma);
683 }
684 kfree(buflist);
685 return NULL;
686 }
687
688 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
689 static void
690 kfree_sgl(SGESimple32_t *sgl, dma_addr_t sgl_dma, struct buflist *buflist, MPT_ADAPTER *ioc)
691 {
692 SGESimple32_t *sg = sgl;
693 struct buflist *bl = buflist;
694 u32 nib;
695 int dir;
696 int n = 0;
697
698 if (le32_to_cpu(sg->FlagsLength) & 0x04000000)
699 dir = PCI_DMA_TODEVICE;
700 else
701 dir = PCI_DMA_FROMDEVICE;
702
703 nib = (le32_to_cpu(sg->FlagsLength) & 0xF0000000) >> 28;
704 while (! (nib & 0x4)) { /* eob */
705 /* skip ignore/chain. */
706 if (nib == 0 || nib == 3) {
707 ;
708 } else if (sg->Address) {
709 dma_addr_t dma_addr;
710 void *kptr;
711 int len;
712
713 dma_addr = le32_to_cpu(sg->Address);
714 kptr = bl->kptr;
715 len = bl->len;
716 pci_unmap_single(ioc->pcidev, dma_addr, len, dir);
717 pci_free_consistent(ioc->pcidev, len, kptr, dma_addr);
718 n++;
719 }
720 sg++;
721 bl++;
722 nib = (le32_to_cpu(sg->FlagsLength) & 0xF0000000) >> 28;
723 }
724
725 /* we're at eob! */
726 if (sg->Address) {
727 dma_addr_t dma_addr;
728 void *kptr;
729 int len;
730
731 dma_addr = le32_to_cpu(sg->Address);
732 kptr = bl->kptr;
733 len = bl->len;
734 pci_unmap_single(ioc->pcidev, dma_addr, len, dir);
735 pci_free_consistent(ioc->pcidev, len, kptr, dma_addr);
736 n++;
737 }
738
739 pci_free_consistent(ioc->pcidev, MAX_SGL_BYTES, sgl, sgl_dma);
740 kfree(buflist);
741 dprintk((KERN_INFO MYNAM "-SG: Free'd 1 SGL buf + %d kbufs!\n", n));
742 }
743
744 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
745 static int
746 mpt_ioctl_rwperf_init(struct mpt_raw_r_w *dest, unsigned long src,
747 char *caller, MPT_ADAPTER **iocpp)
748 {
749 char *myname = "_rwperf_init()";
750 int ioc;
751
752 /* get copy of structure passed from user space */
753 if (copy_from_user(dest, (void*)src, sizeof(*dest))) {
754 printk(KERN_ERR MYNAM "::%s() @%d - Can't copy mpt_raw_r_w data @ %p\n",
755 myname, __LINE__, (void*)src);
756 return -EFAULT; /* (-14) Bad address */
757 } else {
758 dprintk((KERN_INFO MYNAM "-perf: PerfInfo.{ioc,targ,qd,iters,nblks}"
759 ": %d %d %d %d %d\n",
760 dest->iocnum, dest->target,
761 (int)dest->qdepth, dest->iters, dest->nblks ));
762 dprintk((KERN_INFO MYNAM "-perf: PerfInfo.{cache,skip,range,rdwr,seqran}"
763 ": %d %d %d %d %d\n",
764 dest->cache_sz, dest->skip, dest->range,
765 dest->rdwr, dest->seqran ));
766
767 /* Get the MPT adapter id. */
768 if ((ioc = mpt_verify_adapter(dest->iocnum, iocpp)) < 0) {
769 printk(KERN_ERR MYNAM "::%s() @%d - ioc%d not found!\n",
770 myname, __LINE__, dest->iocnum);
771 return -ENXIO; /* (-6) No such device or address */
772 } else {
773 dprintk((MYNAM "-perf: %s using mpt/ioc%x, target %02xh\n",
774 caller, dest->iocnum, dest->target));
775 }
776 }
777
778 return ioc;
779 }
780
781 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
782
783 /* Treat first N blocks of disk as sacred! */
784 #define SACRED_BLOCKS 100
785
786 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
787 static int
788 mpt_ioctl_rwperf(unsigned long arg)
789 {
790 struct mpt_raw_r_w kPerfInfo;
791 /* NOTE: local copy, on stack==KERNEL_SPACE! */
792 u8 target, targetM;
793 u8 lun, lunM;
794 u8 scsiop;
795 int qdepth;
796 int iters;
797 int cache_sz;
798 u32 xferbytes;
799 u32 scsidir;
800 u32 qtag;
801 u32 scsictl;
802 u32 sgdir;
803 u32 blkno;
804 u32 sbphys;
805 SGESimple32_t *sgl;
806 dma_addr_t sgl_dma;
807 struct buflist *buflist;
808 SGESimple32_t *sgOut, *sgIn;
809 int numfrags;
810 u32 *msg;
811 int i;
812 int ioc;
813 MPT_FRAME_HDR *mf;
814 MPT_ADAPTER *iocp;
815 int sgfragcpycnt;
816 int blklo, blkhi;
817 u8 nextchainoffset;
818 u8 *SenseBuf;
819 dma_addr_t SenseBufDMA;
820 char *myname = "_rwperf()";
821
822 dprintk((KERN_INFO "%s - starting...\n", myname));
823
824 /* Validate target device */
825 if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0)
826 return ioc;
827
828 /* Allocate DMA'able memory for the sense buffer. */
829 SenseBuf = pci_alloc_consistent(iocp->pcidev, 256, &SenseBufDMA);
830
831 /* set perf parameters from input */
832 target = kPerfInfo.target & 0x0FF;
833 targetM = target & myMAX_T_MASK;
834 lun = kPerfInfo.lun & 0x1F; // LUN=31 max
835 lunM = lun & myMAX_L_MASK;
836 qdepth = kPerfInfo.qdepth;
837 iters = kPerfInfo.iters;
838 xferbytes = ((u32)kPerfInfo.nblks)<<9;
839
840 DevInUse[targetM][lunM] = 1;
841 DevIosCount[targetM][lunM] = 0;
842
843 cache_sz = kPerfInfo.cache_sz * 1024; // CacheSz in kB!
844
845 /* ToDo: */
846 /* get capacity (?) */
847
848
849 // pre-build, one time, everything we can for speed in the loops below...
850
851 scsiop = 0x28; // default to SCSI READ!
852 scsidir = MPI_SCSIIO_CONTROL_READ; // DATA IN (host<--ioc<--dev)
853 // 02000000
854 qtag = MPI_SCSIIO_CONTROL_SIMPLEQ; // 00000000
855
856 if (xferbytes == 0) {
857 // Do 0-byte READ!!!
858 // IMPORTANT! Need to set no SCSI DIR for this!
859 scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
860 }
861
862 scsictl = scsidir | qtag;
863
864 /*
865 * Set sgdir for DMA transfer.
866 */
867 // sgdir = 0x04000000; // SCSI WRITE
868 sgdir = 0x00000000; // SCSI READ
869
870 if ((sgl = kbuf_alloc_2_sgl(MAX(512,xferbytes), sgdir, &numfrags, &buflist, &sgl_dma, iocp)) == NULL)
871 return -ENOMEM;
872
873 sgfragcpycnt = MIN(10,numfrags);
874 nextchainoffset = 0;
875 if (numfrags > 10)
876 nextchainoffset = 0x1E;
877
878 sbphys = SenseBufDMA;
879
880 rwperf_reset = 0;
881
882 // do { // target-loop
883
884 blkno = SACRED_BLOCKS; // Treat first N blocks as sacred!
885 // FIXME! Skip option
886 blklo = blkno;
887 blkhi = blkno;
888
889 do { // inner-loop
890
891 while ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) {
892 mb();
893 schedule();
894 barrier();
895 }
896 msg = (u32*)mf;
897
898 /* Start piecing the SCSIIORequest together */
899 msg[0] = 0x00000000 | nextchainoffset<<16 | target;
900 msg[1] = 0x0000FF0A; // 255 sense bytes, 10-byte CDB!
901 msg[3] = lun << 8;
902 msg[4] = 0;
903 msg[5] = scsictl;
904
905 // 16 bytes of CDB @ msg[6,7,8,9] are below...
906
907 msg[6] = ( ((blkno & 0xFF000000) >> 8)
908 | ((blkno & 0x00FF0000) << 8)
909 | scsiop );
910 msg[7] = ( (((u32)kPerfInfo.nblks & 0x0000FF00) << 16)
911 | ((blkno & 0x000000FF) << 8)
912 | ((blkno & 0x0000FF00) >> 8) );
913 msg[8] = (kPerfInfo.nblks & 0x00FF);
914 msg[9] = 0;
915
916 msg[10] = xferbytes;
917
918 // msg[11] = 0xD0000100;
919 // msg[12] = sbphys;
920 // msg[13] = 0;
921 msg[11] = sbphys;
922
923 // Copy the SGL...
924 if (xferbytes) {
925 sgOut = (SGESimple32_t*)&msg[12];
926 sgIn = sgl;
927 for (i=0; i < sgfragcpycnt; i++)
928 *sgOut++ = *sgIn++;
929 }
930
931 // fubar! QueueDepth issue!!!
932 while ( !rwperf_reset
933 && (DevIosCount[targetM][lunM] >= MIN(qdepth,64)) )
934 {
935 mb();
936 schedule();
937 barrier();
938 }
939
940 // blkno += kPerfInfo.nblks;
941 // EXP Stuff!
942 // Try optimizing to certain cache size for the target!
943 // by keeping blkno within cache range if at all possible
944 #if 0
945 if ( cache_sz
946 && ((2 * kPerfInfo.nblks) <= (cache_sz>>9))
947 && ((blkno + kPerfInfo.nblks) > ((cache_sz>>9) + SACRED_BLOCKS)) )
948 blkno = SACRED_BLOCKS;
949 else
950 blkno += kPerfInfo.nblks;
951 #endif
952 // Ok, cheat!
953 if (cache_sz && ((blkno + kPerfInfo.nblks) > ((cache_sz>>9) + SACRED_BLOCKS)) )
954 blkno = SACRED_BLOCKS;
955 else
956 blkno += kPerfInfo.nblks;
957
958 if (blkno > blkhi)
959 blkhi = blkno;
960
961 DevIosCount[targetM][lunM]++;
962
963 /*
964 * Finally, post the request
965 */
966 mpt_put_msg_frame(mptctl_id, ioc, mf);
967
968
969 /* let linux breath! */
970 mb();
971 schedule();
972 barrier();
973
974 //dprintk((KERN_DEBUG MYNAM "-perf: inner-loop, cnt=%d\n", iters));
975
976 } while ((--iters > 0) && !rwperf_reset);
977
978 dprintk((KERN_INFO MYNAM "-perf: DbG: blklo=%d, blkhi=%d\n", blklo, blkhi));
979 dprintk((KERN_INFO MYNAM "-perf: target-loop, thisTarget=%d\n", target));
980
981 // // TEMPORARY!
982 // target = 0;
983
984 // } while (target);
985
986
987 if (DevIosCount[targetM][lunM]) {
988 dprintk((KERN_INFO " DbG: DevIosCount[%d][%d]=%d\n",
989 targetM, lunM, DevIosCount[targetM][lunM]));
990 }
991
992 while (DevIosCount[targetM][lunM]) {
993 //dprintk((KERN_DEBUG " DbG: Waiting... DevIosCount[%d][%d]=%d\n",
994 // targetM, lunM, DevIosCount[targetM][lunM]));
995 mb();
996 schedule();
997 barrier();
998 }
999 DevInUse[targetM][lunM] = 0;
1000
1001 pci_free_consistent(iocp->pcidev, 256, SenseBuf, SenseBufDMA);
1002
1003 if (sgl)
1004 kfree_sgl(sgl, sgl_dma, buflist, iocp);
1005
1006 dprintk((KERN_INFO " *** done ***\n"));
1007
1008 return 0;
1009 }
1010
1011 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1012 static int
1013 mpt_ioctl_rwperf_status(unsigned long arg)
1014 {
1015 struct mpt_raw_r_w kPerfInfo;
1016 /* NOTE: local copy, on stack==KERNEL_SPACE! */
1017 MPT_ADAPTER *iocp;
1018 int ioc;
1019 // u8 targ;
1020 // u8 lun;
1021 int T, L;
1022 char *myname = "_rwperf_status()";
1023
1024
1025 dprintk((KERN_INFO "%s - starting...\n", myname));
1026
1027 /* Get a pointer to the MPT adapter. */
1028 if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0)
1029 return ioc;
1030
1031 /* set perf parameters from input */
1032 // targ = kPerfInfo.target & 0xFF;
1033 // lun = kPerfInfo.lun & 0x1F;
1034
1035 for (T=0; T < myMAX_TARGETS; T++)
1036 for (L=0; L < myMAX_LUNS; L++)
1037 if (DevIosCount[T][L]) {
1038 printk(KERN_INFO "%s: ioc%d->00:%02x:%02x"
1039 ", IosCnt=%d\n",
1040 myname, ioc, T, L, DevIosCount[T][L] );
1041 }
1042
1043 return 0;
1044 }
1045
1046 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1047 static int
1048 mpt_ioctl_rwperf_reset(unsigned long arg)
1049 {
1050 struct mpt_raw_r_w kPerfInfo;
1051 /* NOTE: local copy, on stack==KERNEL_SPACE! */
1052 MPT_ADAPTER *iocp;
1053 int ioc;
1054 // u8 targ;
1055 // u8 lun;
1056 int T, L;
1057 int i;
1058 char *myname = "_rwperf_reset()";
1059
1060 dprintk((KERN_INFO "%s - starting...\n", myname));
1061
1062 /* Get MPT adapter id. */
1063 if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0)
1064 return ioc;
1065
1066 /* set perf parameters from input */
1067 // targ = kPerfInfo.target & 0xFF;
1068 // lun = kPerfInfo.lun & 0x1F;
1069
1070 rwperf_reset = 1;
1071 for (i=0; i < 1000000; i++) {
1072 mb();
1073 schedule();
1074 barrier();
1075 }
1076 rwperf_reset = 0;
1077
1078 for (T=0; T < myMAX_TARGETS; T++)
1079 for (L=0; L < myMAX_LUNS; L++)
1080 if (DevIosCount[T][L]) {
1081 printk(KERN_INFO "%s: ioc%d->00:%02x:%02x, "
1082 "IosCnt RESET! (from %d to 0)\n",
1083 myname, ioc, T, L, DevIosCount[T][L] );
1084 DevIosCount[T][L] = 0;
1085 DevInUse[T][L] = 0;
1086 }
1087
1088 return 0;
1089 }
1090
1091 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1092 static int
1093 mpt_ioctl_scsi_cmd(unsigned long arg)
1094 {
1095 return -ENOSYS;
1096 }
1097
1098 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1099
1100 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,51)
1101 #define owner_THIS_MODULE owner: THIS_MODULE,
1102 #else
1103 #define owner_THIS_MODULE
1104 #endif
1105
1106 static struct file_operations mptctl_fops = {
1107 owner_THIS_MODULE
1108 llseek: no_llseek,
1109 read: mptctl_read,
1110 write: mptctl_write,
1111 ioctl: mpt_ioctl,
1112 open: mptctl_open,
1113 release: mptctl_release,
1114 };
1115
1116 static struct miscdevice mptctl_miscdev = {
1117 MPT_MINOR,
1118 MYNAM,
1119 &mptctl_fops
1120 };
1121
1122 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1123
1124 #if defined(__sparc__) && defined(__sparc_v9__) /*{*/
1125
1126 /* The dynamic ioctl32 compat. registry only exists in >2.3.x sparc64 kernels */
1127 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/
1128 extern int register_ioctl32_conversion(unsigned int cmd,
1129 int (*handler)(unsigned int,
1130 unsigned int,
1131 unsigned long,
1132 struct file *));
1133 int unregister_ioctl32_conversion(unsigned int cmd);
1134
1135 struct mpt_fw_xfer32 {
1136 unsigned int iocnum;
1137 unsigned int fwlen;
1138 u32 bufp;
1139 };
1140
1141 #define MPTFWDOWNLOAD32 _IOWR(MPT_MAGIC_NUMBER,15,struct mpt_fw_xfer32)
1142
1143 extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
1144
1145 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1146 static int
1147 sparc32_mptfwxfer_ioctl(unsigned int fd, unsigned int cmd,
1148 unsigned long arg, struct file *filp)
1149 {
1150 struct mpt_fw_xfer32 kfw32;
1151 struct mpt_fw_xfer kfw;
1152 MPT_ADAPTER *iocp = NULL;
1153 int iocnum, iocnumX;
1154 int nonblock = (filp->f_flags & O_NONBLOCK);
1155 int ret;
1156
1157 dprintk((KERN_INFO MYNAM "::sparc32_mptfwxfer_ioctl() called\n"));
1158
1159 if (copy_from_user(&kfw32, (char *)arg, sizeof(kfw32)))
1160 return -EFAULT;
1161
1162 /* Verify intended MPT adapter */
1163 iocnumX = kfw32.iocnum & 0xFF;
1164 if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) ||
1165 (iocp == NULL)) {
1166 printk(KERN_ERR MYNAM "::sparc32_mptfwxfer_ioctl @%d - ioc%d not found!\n",
1167 __LINE__, iocnumX);
1168 return -ENODEV;
1169 }
1170
1171 if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0)
1172 return ret;
1173
1174 kfw.iocnum = iocnum;
1175 kfw.fwlen = kfw32.fwlen;
1176 kfw.bufp = (void *)(unsigned long)kfw32.bufp;
1177
1178 ret = mpt_ioctl_do_fw_download(kfw.iocnum, kfw.bufp, kfw.fwlen);
1179
1180 up(&mptctl_syscall_sem_ioc[iocp->id]);
1181
1182 return ret;
1183 }
1184
1185 #endif /*} linux >= 2.3.x */
1186 #endif /*} sparc */
1187
1188 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1189 int __init mptctl_init(void)
1190 {
1191 int err;
1192 int i;
1193 int where = 1;
1194
1195 show_mptmod_ver(my_NAME, my_VERSION);
1196
1197 for (i=0; i<MPT_MAX_ADAPTERS; i++) {
1198 sema_init(&mptctl_syscall_sem_ioc[i], 1);
1199 }
1200
1201 #if defined(__sparc__) && defined(__sparc_v9__) /*{*/
1202 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/
1203 err = register_ioctl32_conversion(MPTRWPERF, NULL);
1204 if (++where && err) goto out_fail;
1205 err = register_ioctl32_conversion(MPTRWPERF_CHK, NULL);
1206 if (++where && err) goto out_fail;
1207 err = register_ioctl32_conversion(MPTRWPERF_RESET, NULL);
1208 if (++where && err) goto out_fail;
1209 err = register_ioctl32_conversion(MPTFWDOWNLOAD32, sparc32_mptfwxfer_ioctl);
1210 if (++where && err) goto out_fail;
1211 #endif /*} linux >= 2.3.x */
1212 #endif /*} sparc */
1213
1214 if (misc_register(&mptctl_miscdev) == -1) {
1215 printk(KERN_ERR MYNAM ": Can't register misc device [minor=%d].\n", MPT_MINOR);
1216 err = -EBUSY;
1217 goto out_fail;
1218 }
1219 printk(KERN_INFO MYNAM ": Registered with Fusion MPT base driver\n");
1220 printk(KERN_INFO MYNAM ": /dev/%s @ (major,minor=%d,%d)\n",
1221 mptctl_miscdev.name, MISC_MAJOR, mptctl_miscdev.minor);
1222
1223 /*
1224 * Install our handler
1225 */
1226 ++where;
1227 if ((mptctl_id = mpt_register(mptctl_reply, MPTCTL_DRIVER)) <= 0) {
1228 printk(KERN_ERR MYNAM ": ERROR: Failed to register with Fusion MPT base driver\n");
1229 misc_deregister(&mptctl_miscdev);
1230 err = -EBUSY;
1231 goto out_fail;
1232 }
1233
1234 return 0;
1235
1236 out_fail:
1237
1238 #if defined(__sparc__) && defined(__sparc_v9__) /*{*/
1239 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/
1240 printk(KERN_ERR MYNAM ": ERROR: Failed to register ioctl32_conversion!"
1241 " (%d:err=%d)\n", where, err);
1242 unregister_ioctl32_conversion(MPTRWPERF);
1243 unregister_ioctl32_conversion(MPTRWPERF_CHK);
1244 unregister_ioctl32_conversion(MPTRWPERF_RESET);
1245 unregister_ioctl32_conversion(MPTFWDOWNLOAD32);
1246 #endif /*} linux >= 2.3.x */
1247 #endif /*} sparc */
1248
1249 return err;
1250 }
1251
1252 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1253 void mptctl_exit(void)
1254 {
1255
1256 #if defined(__sparc__) && defined(__sparc_v9__) /*{*/
1257 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/
1258 unregister_ioctl32_conversion(MPTRWPERF);
1259 unregister_ioctl32_conversion(MPTRWPERF_CHK);
1260 unregister_ioctl32_conversion(MPTRWPERF_RESET);
1261 unregister_ioctl32_conversion(MPTFWDOWNLOAD32);
1262 #endif /*} linux >= 2.3.x */
1263 #endif /*} sparc */
1264
1265 misc_deregister(&mptctl_miscdev);
1266 printk(KERN_INFO MYNAM ": /dev/%s @ (major,minor=%d,%d)\n",
1267 mptctl_miscdev.name, MISC_MAJOR, mptctl_miscdev.minor);
1268 printk(KERN_INFO MYNAM ": Deregistered from Fusion MPT base driver\n");
1269
1270 mpt_deregister(mptctl_id);
1271 }
1272
1273 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1274
1275 module_init(mptctl_init);
1276 module_exit(mptctl_exit);
1277