1/*
2 * Copyright (c) 2004-2005 HighPoint Technologies, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28/*
29 * hptproc.c  sysctl support
30 */
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/kernel.h>
34#include <sys/malloc.h>
35#include <sys/sysctl.h>
36#include <machine/stdarg.h>
37
38#ifndef __KERNEL__
39#define __KERNEL__
40#endif
41
42#include <dev/hptmv/global.h>
43#include <dev/hptmv/hptintf.h>
44#include <dev/hptmv/osbsd.h>
45#include <dev/hptmv/access601.h>
46
47int hpt_rescan_all(void);
48
49/***************************************************************************/
50
51static char hptproc_buffer[256];
52extern char DRIVER_VERSION[];
53
54#define FORMAL_HANDLER_ARGS struct sysctl_oid *oidp, void *arg1,	\
55	intptr_t arg2, struct sysctl_req *req
56#define REAL_HANDLER_ARGS oidp, arg1, arg2, req
57typedef struct sysctl_req HPT_GET_INFO;
58
59static int
60hpt_set_asc_info(IAL_ADAPTER_T *pAdapter, char *buffer,int length)
61{
62	int orig_length = length+4;
63	PVBus _vbus_p = &pAdapter->VBus;
64	PVDevice	 pArray;
65	PVDevice pSubArray, pVDev;
66	UINT	i, iarray, ichan;
67	struct cam_periph *periph = NULL;
68	intrmask_t oldspl;
69
70#ifdef SUPPORT_ARRAY
71	if (length>=8 && strncmp(buffer, "rebuild ", 8)==0)
72	{
73		buffer+=8;
74		length-=8;
75		if (length>=5 && strncmp(buffer, "start", 5)==0)
76		{
77			oldspl = lock_driver();
78			for(i = 0; i < MAX_ARRAY_PER_VBUS; i++)
79				if ((pArray=ArrayTables(i))->u.array.dArStamp==0)
80					continue;
81				else{
82					if (pArray->u.array.rf_need_rebuild && !pArray->u.array.rf_rebuilding)
83	                    hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapter, pArray,
84							(UCHAR)((pArray->u.array.CriticalMembers || pArray->VDeviceType == VD_RAID_1)? DUPLICATE : REBUILD_PARITY));
85				}
86			unlock_driver(oldspl);
87			return orig_length;
88		}
89		else if (length>=4 && strncmp(buffer, "stop", 4)==0)
90		{
91			oldspl = lock_driver();
92			for(i = 0; i < MAX_ARRAY_PER_VBUS; i++)
93				if ((pArray=ArrayTables(i))->u.array.dArStamp==0)
94					continue;
95				else{
96					if (pArray->u.array.rf_rebuilding)
97					    pArray->u.array.rf_abort_rebuild = 1;
98				}
99			unlock_driver(oldspl);
100			return orig_length;
101		}
102		else if (length>=3 && buffer[1]==','&& buffer[0]>='1'&& buffer[2]>='1')
103		{
104			iarray = buffer[0]-'1';
105	        ichan = buffer[2]-'1';
106
107            if(iarray >= MAX_VDEVICE_PER_VBUS || ichan >= MV_SATA_CHANNELS_NUM) return -EINVAL;
108
109			pArray = _vbus_p->pVDevice[iarray];
110	        if (!pArray || (pArray->vf_online == 0)) return -EINVAL;
111
112            for (i=0;i<MV_SATA_CHANNELS_NUM;i++)
113				if(i == ichan)
114				    goto rebuild;
115
116	        return -EINVAL;
117
118rebuild:
119	        pVDev = &pAdapter->VDevices[ichan];
120	        if(!pVDev->u.disk.df_on_line || pVDev->pParent) return -EINVAL;
121
122	        /* Not allow to use a mounted disk ??? test*/
123			for(i = 0; i < MAX_VDEVICE_PER_VBUS; i++)
124			    if(pVDev == _vbus_p->pVDevice[i])
125			    {
126					periph = hpt_get_periph(pAdapter->mvSataAdapter.adapterId,i);
127					if (periph != NULL && periph->refcount >= 1)
128					{
129						hpt_printk(("Can not use disk used by OS!\n"));
130	                    return -EINVAL;
131					}
132					/* the Mounted Disk isn't delete */
133				}
134
135			switch(pArray->VDeviceType)
136			{
137				case VD_RAID_1:
138				case VD_RAID_5:
139				{
140					pSubArray = pArray;
141loop:
142					oldspl = lock_driver();
143					if(hpt_add_disk_to_array(_VBUS_P VDEV_TO_ID(pSubArray), VDEV_TO_ID(pVDev)) == -1) {
144						unlock_driver(oldspl);
145						return -EINVAL;
146					}
147					pSubArray->u.array.rf_auto_rebuild = 0;
148					pSubArray->u.array.rf_abort_rebuild = 0;
149					hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapter, pSubArray, DUPLICATE);
150					unlock_driver(oldspl);
151					break;
152				}
153				case VD_RAID_0:
154					for (i = 0; (UCHAR)i < pArray->u.array.bArnMember; i++)
155						if(pArray->u.array.pMember[i] && mIsArray(pArray->u.array.pMember[i]) &&
156						   (pArray->u.array.pMember[i]->u.array.rf_broken == 1))
157						{
158							  pSubArray = pArray->u.array.pMember[i];
159							  goto loop;
160						}
161				default:
162					return -EINVAL;
163			}
164			return orig_length;
165		}
166	}
167	else if (length>=7 && strncmp(buffer, "verify ", 7)==0)
168	{
169		buffer+=7;
170		length-=7;
171        if (length>=6 && strncmp(buffer, "start ", 6)==0)
172		{
173            buffer+=6;
174		    length-=6;
175            if (length>=1 && *buffer>='1')
176			{
177				iarray = *buffer-'1';
178				if(iarray >= MAX_VDEVICE_PER_VBUS) return -EINVAL;
179
180				pArray = _vbus_p->pVDevice[iarray];
181				if (!pArray || (pArray->vf_online == 0)) return -EINVAL;
182
183				if(pArray->VDeviceType != VD_RAID_1 && pArray->VDeviceType != VD_RAID_5)
184					return -EINVAL;
185
186				if (!(pArray->u.array.rf_need_rebuild ||
187					pArray->u.array.rf_rebuilding ||
188					pArray->u.array.rf_verifying ||
189					pArray->u.array.rf_initializing))
190				{
191					oldspl = lock_driver();
192					pArray->u.array.RebuildSectors = 0;
193					hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapter, pArray, VERIFY);
194					unlock_driver(oldspl);
195				}
196                return orig_length;
197			}
198		}
199		else if (length>=5 && strncmp(buffer, "stop ", 5)==0)
200		{
201			buffer+=5;
202		    length-=5;
203            if (length>=1 && *buffer>='1')
204			{
205				iarray = *buffer-'1';
206				if(iarray >= MAX_VDEVICE_PER_VBUS) return -EINVAL;
207
208				pArray = _vbus_p->pVDevice[iarray];
209				if (!pArray || (pArray->vf_online == 0)) return -EINVAL;
210				if(pArray->u.array.rf_verifying)
211				{
212					oldspl = lock_driver();
213				    pArray->u.array.rf_abort_rebuild = 1;
214				    unlock_driver(oldspl);
215				}
216			    return orig_length;
217			}
218		}
219	}
220	else
221#ifdef _RAID5N_
222	if (length>=10 && strncmp(buffer, "writeback ", 10)==0) {
223	    	buffer+=10;
224		length-=10;
225		if (length>=1 && *buffer>='0' && *buffer<='1') {
226			_vbus_(r5.enable_write_back) = *buffer-'0';
227			if (_vbus_(r5.enable_write_back))
228				hpt_printk(("RAID5 write back enabled"));
229			return orig_length;
230		}
231	}
232	else
233#endif
234#endif
235	if (0) {} /* just to compile */
236#ifdef DEBUG
237	else if (length>=9 && strncmp(buffer, "dbglevel ", 9)==0) {
238	    	buffer+=9;
239		length-=9;
240		if (length>=1 && *buffer>='0' && *buffer<='3') {
241			hpt_dbg_level = *buffer-'0';
242			return orig_length;
243		}
244	}
245	else if (length>=8 && strncmp(buffer, "disable ", 8)==0) {
246		/* TO DO */
247	}
248#endif
249
250	return -EINVAL;
251}
252
253/*
254 * Since we have only one sysctl node, add adapter ID in the command
255 * line string: e.g. "hpt 0 rebuild start"
256 */
257static int
258hpt_set_info(int length)
259{
260	int retval;
261
262#ifdef SUPPORT_IOCTL
263	PUCHAR ke_area;
264	int err;
265	DWORD dwRet;
266	PHPT_IOCTL_PARAM piop;
267#endif
268	char *buffer = hptproc_buffer;
269	if (length >= 6) {
270		if (strncmp(buffer,"hpt ",4) == 0) {
271			IAL_ADAPTER_T *pAdapter;
272			retval = buffer[4]-'0';
273			for (pAdapter=gIal_Adapter; pAdapter; pAdapter=pAdapter->next) {
274				if (pAdapter->mvSataAdapter.adapterId==retval)
275					return (retval = hpt_set_asc_info(pAdapter, buffer+6, length-6)) >= 0? retval : -EINVAL;
276			}
277			return -EINVAL;
278		}
279#ifdef SUPPORT_IOCTL
280		piop = (PHPT_IOCTL_PARAM)buffer;
281		if (piop->Magic == HPT_IOCTL_MAGIC ||
282			piop->Magic == HPT_IOCTL_MAGIC32) 	{
283			KdPrintE(("ioctl=%d in=%p len=%d out=%p len=%d\n",
284				piop->dwIoControlCode,
285        			piop->lpInBuffer,
286        			piop->nInBufferSize,
287        			piop->lpOutBuffer,
288	        		piop->nOutBufferSize));
289
290			/*
291        	 	 * map buffer to kernel.
292        	 	 */
293        		if (piop->nInBufferSize+piop->nOutBufferSize > PAGE_SIZE) {
294        			KdPrintE(("User buffer too large\n"));
295        			return -EINVAL;
296        		}
297
298        		ke_area = malloc(piop->nInBufferSize+piop->nOutBufferSize, M_DEVBUF, M_NOWAIT);
299				if (ke_area == NULL) {
300					KdPrintE(("Couldn't allocate kernel mem.\n"));
301					return -EINVAL;
302				}
303
304			if (piop->nInBufferSize)
305				copyin((void*)(ULONG_PTR)piop->lpInBuffer, ke_area, piop->nInBufferSize);
306
307			/*
308			  * call kernel handler.
309			  */
310			err = Kernel_DeviceIoControl(&gIal_Adapter->VBus,
311				piop->dwIoControlCode, ke_area, piop->nInBufferSize,
312				ke_area + piop->nInBufferSize, piop->nOutBufferSize, &dwRet);
313
314			if (err==0) {
315				if (piop->nOutBufferSize)
316					copyout(ke_area + piop->nInBufferSize, (void*)(ULONG_PTR)piop->lpOutBuffer, piop->nOutBufferSize);
317
318				if (piop->lpBytesReturned)
319					copyout(&dwRet, (void*)(ULONG_PTR)piop->lpBytesReturned, sizeof(DWORD));
320
321				free(ke_area, M_DEVBUF);
322				return length;
323			}
324			else  KdPrintW(("Kernel_ioctl(): return %d\n", err));
325
326			free(ke_area, M_DEVBUF);
327            		return -EINVAL;
328		} else 	{
329    		KdPrintW(("Wrong signature: %x\n", piop->Magic));
330    		return -EINVAL;
331		}
332#endif
333	}
334
335	return -EINVAL;
336}
337
338#define shortswap(w) ((WORD)((w)>>8 | ((w) & 0xFF)<<8))
339
340static void
341get_disk_name(char *name, PDevice pDev)
342{
343	int i;
344	MV_SATA_CHANNEL *pMvSataChannel = pDev->mv;
345	IDENTIFY_DATA2 *pIdentifyData = (IDENTIFY_DATA2 *)pMvSataChannel->identifyDevice;
346
347	for (i = 0; i < 10; i++)
348		((WORD*)name)[i] = shortswap(pIdentifyData->ModelNumber[i]);
349	name[20] = '\0';
350}
351
352static int
353hpt_copy_info(HPT_GET_INFO *pinfo, char *fmt, ...)
354{
355	int printfretval;
356	va_list ap;
357
358	if(fmt == NULL) {
359		*hptproc_buffer = 0;
360		return (SYSCTL_OUT(pinfo, hptproc_buffer, 1));
361	}
362	else
363	{
364		va_start(ap, fmt);
365		printfretval = vsnprintf(hptproc_buffer, sizeof(hptproc_buffer), fmt, ap);
366		va_end(ap);
367		return(SYSCTL_OUT(pinfo, hptproc_buffer, strlen(hptproc_buffer)));
368	}
369}
370
371static void
372hpt_copy_disk_info(HPT_GET_INFO *pinfo, PVDevice pVDev, UINT iChan)
373{
374	char name[32], arrayname[16], *status;
375
376	get_disk_name(name, &pVDev->u.disk);
377
378	if (!pVDev->u.disk.df_on_line)
379		status = "Disabled";
380	else if (pVDev->VDeviceType==VD_SPARE)
381		status = "Spare   ";
382	else
383		status = "Normal  ";
384
385#ifdef SUPPORT_ARRAY
386	if(pVDev->pParent) {
387		memcpy(arrayname, pVDev->pParent->u.array.ArrayName, MAX_ARRAY_NAME);
388		if (pVDev->pParent->u.array.CriticalMembers & (1<<pVDev->bSerialNumber))
389			status = "Degraded";
390	}
391	else
392#endif
393		arrayname[0]=0;
394
395	hpt_copy_info(pinfo, "Channel %d  %s  %5dMB  %s %s\n",
396		iChan+1,
397		name, pVDev->VDeviceCapacity>>11, status, arrayname);
398}
399
400#ifdef SUPPORT_ARRAY
401static void
402hpt_copy_array_info(HPT_GET_INFO *pinfo, int nld, PVDevice pArray)
403{
404	int i;
405	char *sType=0, *sStatus=0;
406	char buf[32];
407    PVDevice pTmpArray;
408
409	switch (pArray->VDeviceType) {
410		case VD_RAID_0:
411			for (i = 0; (UCHAR)i < pArray->u.array.bArnMember; i++)
412		  		if(pArray->u.array.pMember[i])	{
413			  		if(mIsArray(pArray->u.array.pMember[i]))
414				 		sType = "RAID 1/0   ";
415			  			/* TO DO */
416			  		else
417				 		sType = "RAID 0     ";
418			  		break;
419		  		}
420			break;
421
422		case VD_RAID_1:
423			sType = "RAID 1     ";
424			break;
425
426		case VD_JBOD:
427			sType = "JBOD       ";
428			break;
429
430		case VD_RAID_5:
431       		sType = "RAID 5     ";
432			break;
433
434		default:
435			sType = "N/A        ";
436			break;
437	}
438
439	if (pArray->vf_online == 0)
440		sStatus = "Disabled";
441	else if (pArray->u.array.rf_broken)
442		sStatus = "Critical";
443	for (i = 0; (UCHAR)i < pArray->u.array.bArnMember; i++)
444	{
445		if (!sStatus)
446		{
447			if(mIsArray(pArray->u.array.pMember[i]))
448                		pTmpArray = pArray->u.array.pMember[i];
449			else
450			   	pTmpArray = pArray;
451
452			if (pTmpArray->u.array.rf_rebuilding) {
453#ifdef DEBUG
454				sprintf(buf, "Rebuilding %lldMB", (pTmpArray->u.array.RebuildSectors>>11));
455#else
456				sprintf(buf, "Rebuilding %d%%", (UINT)((pTmpArray->u.array.RebuildSectors>>11)*100/((pTmpArray->VDeviceCapacity/(pTmpArray->u.array.bArnMember-1))>>11)));
457#endif
458				sStatus = buf;
459			}
460			else if (pTmpArray->u.array.rf_verifying) {
461				sprintf(buf, "Verifying %d%%", (UINT)((pTmpArray->u.array.RebuildSectors>>11)*100/((pTmpArray->VDeviceCapacity/(pTmpArray->u.array.bArnMember-1))>>11)));
462				sStatus = buf;
463			}
464			else if (pTmpArray->u.array.rf_need_rebuild)
465				sStatus = "Critical";
466			else if (pTmpArray->u.array.rf_broken)
467				sStatus = "Critical";
468
469			if(pTmpArray == pArray) goto out;
470		}
471		else
472			goto out;
473	}
474out:
475	if (!sStatus) sStatus = "Normal";
476	hpt_copy_info(pinfo, "%2d  %11s  %-20s  %5lldMB  %-16s", nld, sType, pArray->u.array.ArrayName, pArray->VDeviceCapacity>>11, sStatus);
477}
478#endif
479
480static int
481hpt_get_info(IAL_ADAPTER_T *pAdapter, HPT_GET_INFO *pinfo)
482{
483	PVBus _vbus_p = &pAdapter->VBus;
484	struct cam_periph *periph = NULL;
485	UINT channel,j,i;
486	PVDevice pVDev;
487
488#ifndef FOR_DEMO
489	if (pAdapter->beeping) {
490		intrmask_t oldspl = lock_driver();
491		pAdapter->beeping = 0;
492		BeepOff(pAdapter->mvSataAdapter.adapterIoBaseAddress);
493		unlock_driver(oldspl);
494	}
495#endif
496
497	hpt_copy_info(pinfo, "Controller #%d:\n\n", pAdapter->mvSataAdapter.adapterId);
498
499	hpt_copy_info(pinfo, "Physical device list\n");
500	hpt_copy_info(pinfo, "Channel    Model                Capacity  Status   Array\n");
501	hpt_copy_info(pinfo, "-------------------------------------------------------------------\n");
502
503    for (channel = 0; channel < MV_SATA_CHANNELS_NUM; channel++)
504	{
505		pVDev = &(pAdapter->VDevices[channel]);
506		if(pVDev->u.disk.df_on_line)
507			 hpt_copy_disk_info(pinfo, pVDev, channel);
508	}
509
510	hpt_copy_info(pinfo, "\nLogical device list\n");
511	hpt_copy_info(pinfo, "No. Type         Name                 Capacity  Status            OsDisk\n");
512	hpt_copy_info(pinfo, "--------------------------------------------------------------------------\n");
513
514	j=1;
515	for(i = 0; i < MAX_VDEVICE_PER_VBUS; i++){
516        pVDev = _vbus_p->pVDevice[i];
517		if(pVDev){
518			j=i+1;
519#ifdef SUPPORT_ARRAY
520			if (mIsArray(pVDev))
521			{
522		is_array:
523				hpt_copy_array_info(pinfo, j, pVDev);
524			}
525			else
526#endif
527			{
528				char name[32];
529				/* it may be add to an array after driver loaded, check it */
530#ifdef SUPPORT_ARRAY
531				if (pVDev->pParent)
532					/* in this case, pVDev can only be a RAID 1 source disk. */
533					if (pVDev->pParent->VDeviceType==VD_RAID_1 && pVDev==pVDev->pParent->u.array.pMember[0])
534						goto is_array;
535#endif
536				get_disk_name(name, &pVDev->u.disk);
537
538				hpt_copy_info(pinfo, "%2d  %s  %s  %5dMB  %-16s",
539					j, "Single disk", name, pVDev->VDeviceCapacity>>11,
540					/* gmm 2001-6-19: Check if pDev has been added to an array. */
541					((pVDev->pParent) ? "Unavailable" : "Normal"));
542			}
543			periph = hpt_get_periph(pAdapter->mvSataAdapter.adapterId, i);
544			if (periph == NULL)
545				hpt_copy_info(pinfo,"  %s\n","not registered");
546			else
547				hpt_copy_info(pinfo,"  %s%d\n", periph->periph_name, periph->unit_number);
548		 }
549	}
550	return 0;
551}
552
553static __inline int
554hpt_proc_in(FORMAL_HANDLER_ARGS, int *len)
555{
556	int i, error=0;
557
558	*len = 0;
559	if ((req->newlen - req->newidx) >= sizeof(hptproc_buffer)) {
560		error = EINVAL;
561	} else {
562		i = (req->newlen - req->newidx);
563		error = SYSCTL_IN(req, hptproc_buffer, i);
564		if (!error)
565			*len = i;
566		(hptproc_buffer)[i] = '\0';
567	}
568	return (error);
569}
570
571static int
572hpt_status(FORMAL_HANDLER_ARGS)
573{
574	int length, error=0, retval=0;
575	IAL_ADAPTER_T *pAdapter;
576
577	error = hpt_proc_in(REAL_HANDLER_ARGS, &length);
578
579    if (req->newptr != NULL)
580	{
581		if (error || length == 0)
582		{
583    		KdPrint(("error!\n"));
584    		retval = EINVAL;
585    		goto out;
586		}
587
588		if (hpt_set_info(length) >= 0)
589			retval = 0;
590		else
591			retval = EINVAL;
592		goto out;
593    }
594
595	hpt_copy_info(req, "%s Version %s\n", DRIVER_NAME, DRIVER_VERSION);
596	for (pAdapter=gIal_Adapter; pAdapter; pAdapter=pAdapter->next) {
597		if (hpt_get_info(pAdapter, req) < 0) {
598			retval = EINVAL;
599			break;
600		}
601	}
602
603	hpt_copy_info(req, NULL);
604	goto out;
605
606out:
607	return (retval);
608}
609
610
611#define xhptregister_node(name) hptregister_node(name)
612
613#if (__FreeBSD_version < 500043)
614#define hptregister_node(name) \
615	SYSCTL_NODE(, OID_AUTO,	name, CTLFLAG_RW, 0, "Get/Set " #name " state root node") \
616	SYSCTL_OID(_ ## name, OID_AUTO, status, CTLTYPE_STRING|CTLFLAG_RW, \
617	NULL, 0, hpt_status, "A", "Get/Set " #name " state")
618#else
619#define hptregister_node(name) \
620	SYSCTL_NODE(, OID_AUTO,	name, CTLFLAG_RW, 0, "Get/Set " #name " state root node"); \
621	SYSCTL_OID(_ ## name, OID_AUTO, status, CTLTYPE_STRING|CTLFLAG_RW, \
622	NULL, 0, hpt_status, "A", "Get/Set " #name " state");
623#endif
624
625xhptregister_node(PROC_DIR_NAME);
626