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