More Cisco specific stuff

FossilOrigin-Name: ba1217e4069e946c852d379efe8a1c7bc95a6a545c85853ae54cdc3786b60dac
This commit is contained in:
7u83@mail.ru 2015-03-14 09:15:12 +00:00
parent 140af914b2
commit ac8b979e84
20 changed files with 222 additions and 135 deletions

View File

@ -545,6 +545,7 @@ static int wtpman_join(void *arg,time_t timer)
memcpy (radioinfo.rmac, cwrmsg->rmac,8);
struct ac_info * acinfo = get_acinfo();
sleep(10);
int result_code = 0;
cw_dbg(DBG_CW_MSG,"Sending join response to %s",CLIENT_IP);

View File

@ -128,8 +128,10 @@ CAPWAPOBJS= \
cw_readelem_vendor_specific_payload.o \
cw_readelem_capwap_local_ip_addr.o \
cw_readelem_wtp_reboot_statistics.o\
cwmsg_addelem_vendor_cisco_ap_timesync.o \
lw_checksum.o
cwmsg_addelem_vendor_cisco_ap_timesync.o \
cwmsg_addelem_vendor_cisco_mwar_addr.o \
lw_checksum.o \
#cwmsg_addelem_session_id.o
# cwsend_image_data_request.o
# cwmsg_set_control_header.o

View File

@ -1,6 +1,10 @@
#ifndef __BSTR_H
#define __BSTR_H
#include <stdint.h>
typedef uint8_t* bstr_t;
extern uint8_t * bstr_create(uint8_t *data, uint8_t len);
extern uint8_t * bstr_create_from_cfgstr(const char * s);
@ -9,3 +13,7 @@ extern uint8_t * bstr_replace( uint8_t ** dst, uint8_t * bstr);
#define bstr_len(s) (*(s))
#define bstr_data(s) (s+1)
#endif

View File

@ -29,5 +29,4 @@ uint8_t * bstr_create(uint8_t *data, uint8_t len)
*str=len;
memcpy(str+1,data,len);
return str;
}

View File

@ -36,9 +36,8 @@ uint8_t * bstr_create_from_cfgstr(const char * s)
if (l<=2)
return bstr_create((uint8_t*)s,l+1);
if (s[1]=='.'){
if (s[1]=='.')
return bstr_create((uint8_t*)s+1,l);
}
if (s[1]=='x'){
uint8_t * ns=0;
@ -71,7 +70,6 @@ uint8_t * bstr_create_from_cfgstr(const char * s)
}
return NULL;
}

View File

@ -16,17 +16,28 @@
*/
#ifndef __CAPWAP_CISCO_H
#define __CAPWAP_CISCO_H
#include <string.h>
#define CWVENDOR_CISCO_MWAR_ADDR 2
#define CWVENDOR_CISCO_RAD 3
#define CWVENDOR_CISCO_RAD_SLOT 4
#define CWVENDOR_CISCO_RAD_NAME 5
#define CWVENDOR_CISCO_MWAR 6
#define CWVENDOR_CISCO_AP_GROUP_NAME 124
#define CWVENDOR_CISCO_AP_TIMESYNC 151
extern void cwmsg_addelem_vendor_cisco_ap_timesync(struct cwmsg * cwmsg);
/* rad_name payload */
#define cwmsg_addelem_vendor_cisco_rad_name(cwmsg,str) \
cwmsg_addelem_vendor_specific_payload(cwmsg,CW_VENDOR_ID_CISCO, \
CWVENDOR_CISCO_RAD_NAME,str,strlen((char*)str))
#endif

View File

@ -17,6 +17,7 @@ struct cwmsg{
int rid;
int seqnum;
int type;
int capwap_mode;
};
struct conn;
@ -37,4 +38,9 @@ extern void cwmsg_addelem_image_identifier(struct cwmsg *msg,uint32_t vendor_id,
extern void cwmsg_addelem_radio_operational_state(struct cwmsg * cwmsg, struct radioinfo * ri);
extern void cwmsg_addelem_vendor_cosco_mwar_addr(struct cwmsg *msg, struct conn *conn);
#define cwmsg_addelem_session_id(msg,session_id) \
cwmsg_addelem(msg,CWMSGELEM_SESSION_ID,bstr_data(session_id),bstr_len(session_id));
#endif

View File

@ -30,26 +30,41 @@
void cwmsg_addelem_cw_local_ip_addr(struct cwmsg *msg, struct conn * conn)
{
struct sockaddr_storage a;
socklen_t alen = sizeof(struct sockaddr_storage);
getsockname (conn->sock,(struct sockaddr *)&a,&alen);
int cw_mode = msg->capwap_mode;
switch (((struct sockaddr*)&a)->sa_family){
case AF_INET:
{
struct sockaddr_in * sain = (struct sockaddr_in*)&a;
cwmsg_addelem(msg,CWMSGELEM_CAPWAP_LOCAL_IPV4_ADDRESS,(uint8_t*)&sain->sin_addr,4);
}
int id;
if (cw_mode == CWMODE_CISCO)
id = CWMSGELEM_WTP_IPV4_IP_ADDR;
else
id = CWMSGELEM_CAPWAP_LOCAL_IPV4_ADDRESS;
cwmsg_addelem(msg,id,(uint8_t*)&sain->sin_addr,4);
break;
#ifdef WITH_IPV6
}
case AF_INET6:
{
int id;
if (cw_mode == CWMODE_CISCO)
id = CWMSGELEM_WTP_IPV6_IP_ADDR;
else
id = CWMSGELEM_CAPWAP_LOCAL_IPV6_ADDRESS;
struct sockaddr_in6 * sain = (struct sockaddr_in6*)&a;
cwmsg_addelem(msg,CWMSGELEM_CAPWAP_LOCAL_IPV6_ADDRESS,(uint8_t*)&sain->sin6_addr,16);
return cwmsg_addelem(msg,id,(uint8_t*)&sain->sin6_addr,16);
}
break;
#endif
}
}
}

View File

@ -5,23 +5,12 @@
#include "bstr.h"
static inline int wtpdesc_addsubelem(uint8_t * dst,uint8_t type,uint32_t vendorid,uint8_t * str)
static inline int wtpdesc_addsubelem(uint8_t * dst,uint8_t type,uint32_t vendorid,bstr_t str)
{
// printf("add subelem\n");
int l;
*((uint32_t*)(dst))=htonl(vendorid);
// printf("htonl done\n");
// if (len==-1)
// l=strlen((char*)str);
// else
// l=len;S
l = bstr_len(str);
// printf("strlne got %d\n",l);
*((uint32_t*)(dst+4))=htonl((type<<16)|l);
// printf("memcopy str %d\n",l);
memcpy(dst+8,bstr_data(str),l);
return l+8;
}
@ -36,50 +25,40 @@ void cwmsg_addelem_wtp_descriptor(struct cwmsg * cwmsg, struct wtpinfo * wtpinfo
*(d+1)=wtpinfo->radios_in_use;
len=2;
switch (wtpinfo->capwap_mode){
switch (cwmsg->capwap_mode){
case CWMODE_CISCO:
*((uint16_t*)(d+len))=0;
len+=2;
break;
default:
/* number of encryption elemnts */
*(d+len)=0;
len += 1;
/* encryption elements */
/* *(d+len)=CWTH_WBID_IEEE80211;
uint16_t val = 0;
*((uint16_t*)(d+len+1))=htons(val);
len+=3;
*/
break;
}
/* number of encryption elemnts */
// *(d+len)=1;
// len+=1;
// *(d+len)=0;
// len+=1;
/* encryption elements */
/* hardware version subelem*/
len+=wtpdesc_addsubelem(d+len,CWMSGSUBELEM_WTP_DESCRIPTOR_HARDWARE_VERSION,
wtpinfo->hardware_vendor_id,wtpinfo->hardware_version);
/* *(d+len)=CWTH_WBID_IEEE80211;
uint16_t val = 0;
*((uint16_t*)(d+len+1))=htons(val);
len+=3;
*/
/* uint8_t hww[2];
hww[0]=0x1c;
hww[1]=0;
*/
/* software subelem*/
/* software version subelem*/
len+=wtpdesc_addsubelem(d+len,CWMSGSUBELEM_WTP_DESCRIPTOR_SOFTWARE_VERSION,
wtpinfo->software_vendor_id,wtpinfo->software_version);
/* hardware subelem*/
len+=wtpdesc_addsubelem(d+len,CWMSGSUBELEM_WTP_DESCRIPTOR_HARDWARE_VERSION,
wtpinfo->hardware_vendor_id,wtpinfo->hardware_version);
/*
len+=wtpdesc_addsubelem(d+len,CWMSGSUBELEM_WTP_DESCRIPTOR_HARDWARE_VERSION,
wtpinfo->hardware_vendor_id,hww,2);
*/
/* bootloader subelem*/
/* bootloader version subelem*/
len+=wtpdesc_addsubelem(d+len,CWMSGSUBELEM_WTP_DESCRIPTOR_BOOTLOADER_VERSION,
wtpinfo->bootloader_vendor_id,wtpinfo->bootloader_version);
cwmsg_addelem(cwmsg,CWMSGELEM_WTP_DESCRIPTOR,d,len);
}

View File

@ -31,26 +31,13 @@ void cwmsg_addelem_wtp_radio_info(struct cwmsg * msg,struct radioinfo *radioinfo
}
/*
void cwmsg_addelem_wtp_radio_infos(struct cwmsg * msg,struct wtpinfo * wtpinfo)
{
int i;
for (i=1; i<30; i++)
{
if (wtpinfo->radioinfo[i].rid!=0)
cwmsg_addelem_wtp_radio_info(msg,&wtpinfo->radioinfo[i]);
}
}
*/
void cwmsg_addelem_wtp_radio_infos(struct cwmsg * msg,struct radioinfo * radioinfos)
{
int i;
for (i=1; i<=30; i++)
for (i=0; i<2; i++)
{
if (radioinfos[i].rid!=0)
// if (radioinfos[i].rid!=0)
cwmsg_addelem_wtp_radio_info(msg,&radioinfos[i]);
}

View File

@ -17,6 +17,9 @@
*/
#include "capwap.h"
#include "capwap_cisco.h"
#include "conn.h"
#include "cwmsg.h"
@ -26,42 +29,50 @@ int cwsend_discovery_request(struct conn * conn,struct radioinfo * radioinfo,str
struct cwmsg cwmsg;
cwmsg_init(&cwmsg,buffer,CWMSG_DISCOVERY_REQUEST,conn_get_next_seqnum(conn),NULL /*radioinfo*/);
cwmsg.capwap_mode=conn->capwap_mode;
/* Mandatory elements */
/* discovery type */
cwmsg_addelem(&cwmsg,CWMSGELEM_DISCOVERY_TYPE,&wtpinfo->discovery_type,sizeof(uint8_t));
/* wtp board data */
cwmsg_addelem_wtp_board_data(&cwmsg,wtpinfo);
// cwmsg_addelem_wtp_descriptor(&cwmsg,wtpinfo);
/* wtp descriptor */
cwmsg_addelem_wtp_descriptor(&cwmsg,wtpinfo);
/* wtp frame tunnel mode */
cwmsg_addelem(&cwmsg,CWMSGELEM_WTP_FRAME_TUNNEL_MODE,&wtpinfo->frame_tunnel_mode,sizeof(uint8_t));
/* mac type */
cwmsg_addelem(&cwmsg,CWMSGELEM_WTP_MAC_TYPE,&wtpinfo->mac_type,sizeof(uint8_t));
//cwmsg_addelem(&cwmsg,CWMSGELEM_CAPWAP_LOCAL_IPV4_ADDRESS);
//cwmsg_addelem_cw_local_ip_addr(&cwmsg,conn);
/* radio infos */
cwmsg_addelem_wtp_radio_infos(&cwmsg,wtpinfo->radioinfo);
if (conn->mtu_discovery)
cwmsg_addelem_mtu_discovery_padding(&cwmsg,conn);
/* Non-mandatory elements */
switch (cwmsg.capwap_mode){
case CWMODE_CISCO:
cwmsg_addelem_vendor_cisco_rad_name(&cwmsg,(uint8_t*)wtpinfo->name);
break;
default:
if (conn->mtu_discovery)
cwmsg_addelem_mtu_discovery_padding(&cwmsg,conn);
}
//uint8_t zven[] = {0xBF, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,0x66,0x69,0x73,0x68,0x00,0x02,0xFC,0xF5,0x28,0xCA,0xAE,0xE4,0x00,0x03,0x10,0x10,
//0x04, 0x10, 0x00,0x00,0x10,0x00,0x00,0x06,0xFC,0xF5,0x28,0xCA,0xAE,0xE5,0xC4,0x2E,0xC4,0x2E,0xC4,0x2E,0xC4,0x2E,0xC4,0x2E,
//0xC4,0x2E,0xC4,0x2E,0xC4,0x2E,0xC4,0x2E };
/*
uint8_t zven [] = {
// 0x00, 00 03 7A 00 02
0x22, 0xE0, 00, 00, 00, 00, 00, 00, 00, 0x01, 0x66, 0x69, 0x73,0x68,0x00,0x02,0xFC,0xF5,0x28,0xCA,0xAE,0xE4,0x00,0x03,0x10,0x10 ,
0x04,0x10,0x00, 00, 0x10, 00, 00,0x06,0xFC,0xF5,0x28,0xCA,0xAE,0xE5,0xAB,0x37,0xAB,0x37,0xAB,0x37,0xAB,0x37,0xAB,0x37,0xAB,0x37,0xAB,0x37,0xAB,0x37,0xAB,0x37,
00, 0x07, 00, 00, 0x27,0x11,0x00,0x08,0x00,0x00 };
*/
// cwmsg_addelem_vendor_specific_payload(&cwmsg,890,2,zven,sizeof(zven));
return conn_send_cwmsg(conn,&cwmsg);
}

View File

@ -22,34 +22,61 @@
#include "conn.h"
#include "cwmsg.h"
int cwsend_join_request(struct conn * conn,struct radioinfo * radioinfo,struct wtpinfo * wtpinfo)
int cwsend_join_request(struct conn *conn, struct radioinfo *radioinfo, struct wtpinfo *wtpinfo)
{
uint8_t buffer[CWMSG_MAX_SIZE];
struct cwmsg cwmsg;
cwmsg_init(&cwmsg,buffer,CWMSG_JOIN_REQUEST,conn_get_next_seqnum(conn),radioinfo);
cwmsg_init(&cwmsg, buffer, CWMSG_JOIN_REQUEST, conn_get_next_seqnum(conn), radioinfo);
cwmsg.capwap_mode = conn->capwap_mode;
cwmsg_addelem(&cwmsg,CWMSGELEM_LOCATION_DATA,wtpinfo->location,strlen((char*)wtpinfo->location));
cwmsg_addelem_wtp_board_data(&cwmsg,wtpinfo);
cwmsg_addelem_wtp_descriptor(&cwmsg,wtpinfo);
cwmsg_addelem(&cwmsg,CWMSGELEM_WTP_NAME,wtpinfo->name,strlen((char*)wtpinfo->name));
/* Mandatory elements */
if (wtpinfo->session_id_len>0){
cwmsg_addelem(&cwmsg,CWMSGELEM_SESSION_ID,wtpinfo->session_id,wtpinfo->session_id_len);
/* location data */
cwmsg_addelem(&cwmsg, CWMSGELEM_LOCATION_DATA, wtpinfo->location,
strlen((char *) wtpinfo->location));
/* wtp board data */
cwmsg_addelem_wtp_board_data(&cwmsg, wtpinfo);
/* wtp descriptor */
cwmsg_addelem_wtp_descriptor(&cwmsg, wtpinfo);
/* wtp name */
cwmsg_addelem(&cwmsg, CWMSGELEM_WTP_NAME, wtpinfo->name, strlen((char *) wtpinfo->name));
/* session id */
cwmsg_addelem_session_id(&cwmsg, wtpinfo->session_id);
/* frame tunnel mode */
cwmsg_addelem(&cwmsg, CWMSGELEM_WTP_FRAME_TUNNEL_MODE, &wtpinfo->frame_tunnel_mode,
sizeof(uint8_t));
/* WTP MAC type */
cwmsg_addelem(&cwmsg, CWMSGELEM_WTP_MAC_TYPE, &wtpinfo->mac_type, sizeof(uint8_t));
/* WTP radio information elements */
cwmsg_addelem_wtp_radio_infos(&cwmsg, wtpinfo->radioinfo);
switch (conn->capwap_mode) {
case CWMODE_CISCO:
cwmsg_addelem_vendor_cisco_mwar_addr(&cwmsg,conn);
break;
default:
/* ECN support */
cwmsg_addelem(&cwmsg, CWMSGELEM_ECN_SUPPORT, &wtpinfo->ecn_support,
sizeof(uint8_t));
}
cwmsg_addelem(&cwmsg,CWMSGELEM_WTP_FRAME_TUNNEL_MODE,&wtpinfo->frame_tunnel_mode,sizeof(uint8_t));
cwmsg_addelem(&cwmsg,CWMSGELEM_WTP_MAC_TYPE,&wtpinfo->mac_type,sizeof(uint8_t));
cwmsg_addelem_wtp_radio_infos(&cwmsg,wtpinfo->radioinfo);
if (wtpinfo->capwap_mode != CWMODE_CISCO){
cwmsg_addelem(&cwmsg,CWMSGELEM_ECN_SUPPORT,&wtpinfo->ecn_support,sizeof(uint8_t));
cwmsg_addelem_cw_local_ip_addr(&cwmsg,conn);
}
/* local ip address */
cwmsg_addelem_cw_local_ip_addr(&cwmsg, conn);
/* Non-mandatory elements */
/* maximum message length */
uint16_t l = htons(wtpinfo->max_msg_len);
cwmsg_addelem(&cwmsg,CWMSGELEM_MAXIMUM_MESSAGE_LENGTH,(uint8_t*)&l,sizeof(l));
cwmsg_addelem(&cwmsg, CWMSGELEM_MAXIMUM_MESSAGE_LENGTH, (uint8_t *) & l, sizeof(l));
return conn_send_cwmsg(conn,&cwmsg);
return conn_send_cwmsg(conn, &cwmsg);
}

View File

@ -23,7 +23,7 @@
#include <sys/socket.h>
#include "radioinfo.h"
#include "bstr.h"
struct wtp_reboot_statistics{
@ -41,7 +41,6 @@ struct wtp_reboot_statistics{
/* structure to hold info about a wtp */
struct wtpinfo{
int capwap_mode;
uint8_t *ac_name;
@ -61,8 +60,10 @@ struct wtpinfo{
uint8_t frame_tunnel_mode;
uint8_t mac_type;
uint8_t * session_id;
int session_id_len;
bstr_t session_id;
// int session_id_len;
struct radioinfo radioinfo[31];

View File

@ -205,8 +205,8 @@ int wtpinfo_print(char *str, struct wtpinfo * wtpinfo)
s+=sprintf (s,"\tSession ID: ");
if (wtpinfo->session_id) {
int i;
for (i=0; i<wtpinfo->session_id_len; i++)
s+=sprintf(s,"%02X",wtpinfo->session_id[i]);
for (i=0; i<bstr_len(wtpinfo->session_id); i++)
s+=sprintf(s,"%02X",bstr_data(wtpinfo->session_id)[i]);
}
else
s+=sprintf(s,"Not set");

View File

@ -13,9 +13,11 @@ int wtpinfo_readelem_session_id(struct wtpinfo * wtpinfo, int type, uint8_t * ms
if (type != CWMSGELEM_SESSION_ID)
return 0;
wtpinfo->session_id = realloc(wtpinfo->session_id,len);
memcpy(wtpinfo->session_id,msgelem,len);
wtpinfo->session_id_len=len;
wtpinfo->session_id = bstr_create(msgelem,len);
// wtpinfo->session_id = realloc(wtpinfo->session_id,len);
// memcpy(wtpinfo->session_id,msgelem,len);
// wtpinfo->session_id_len=len;
return 1;
}

View File

@ -169,6 +169,20 @@ int read_config(const char * filename){
bstr_replace(&conf_software_version,s);
}
str = uci_lookup_option_string(ctx,section,"hardware_version");
if (str){
uint8_t * s = bstr_create_from_cfgstr(str);
bstr_replace(&conf_hardware_version,s);
}
str = uci_lookup_option_string(ctx,section,"bootloader_version");
if (str){
uint8_t * s = bstr_create_from_cfgstr(str);
bstr_replace(&conf_bootloader_version,s);
}
str = uci_lookup_option_string(ctx,section,"serial_no");
if (str){
uint8_t * s = bstr_create_from_cfgstr(str);

View File

@ -152,6 +152,7 @@ static int do_discover_conn(struct conn * conn,struct discovery_info * di)
int rc;
do {
conn->capwap_mode=CWMODE_CISCO;
rc = cwsend_discovery_request(conn,&ri,wtpinfo);
if (rc<0){
if (errno == EINTR)

View File

@ -44,9 +44,16 @@ int join_state(struct conn * conn)
rc = cwsend_join_request(conn,&ri,wtpinfo);
printf("Seqnum after = %i\n",conn->seqnum);
struct cwrmsg * cwrmsg = conn_get_message(conn);
struct cwrmsg * cwrmsg;
// do {
cwrmsg = conn_get_message(conn);
printf("Received %08p\n",cwrmsg);
// }while(cwrmsg==0);
exit(0);
// cw_log_debug0("Received message %i",cwrmsg->seqnum);
@ -86,6 +93,10 @@ int join(struct sockaddr *sa)
}
struct conn * conn = get_conn();
conn->capwap_mode = CWMODE_CISCO;
conn->sock=sockfd;
sock_copyaddr(&conn->addr,sa);

View File

@ -5,6 +5,7 @@
#include "capwap/wtpinfo.h"
#include "capwap/acinfo.h"
#include "capwap/conn.h"
#include "capwap/capwap_ieee80211.h"
#include "wtp_conf.h"
#include "wtp_interface.h"
@ -18,17 +19,16 @@ struct wtpinfo * get_wtpinfo()
wtpinfo=malloc(sizeof(struct wtpinfo));
memset(wtpinfo,0,sizeof(struct wtpinfo));
wtpinfo->capwap_mode=CWMODE_CISCO;
wtpinfo->name = (uint8_t*)"wtp";
wtpinfo->location = (uint8_t*)"Unknown";
wtpinfo->name = (uint8_t*)"wtpXY";
wtpinfo->location = (uint8_t*)"default location";
wtpinfo->max_radios=wtpdrv_get_num_radios();
/* int i;
int i;
for (i=0; i<wtpdrv_get_num_radios(); i++){
wtpdrv_get_radioinfo(i+1,&wtpinfo.radioinfo[i+1]);
wtpdrv_get_radioinfo(i,&(wtpinfo->radioinfo[i]));
}
*/
wtpinfo->serial_no=conf_serial_no;
wtpinfo->vendor_id=conf_vendor_id;
@ -41,6 +41,11 @@ struct wtpinfo * get_wtpinfo()
wtpinfo->hardware_vendor_id=CW_VENDOR_ID_CISCO;
wtpinfo->software_version=conf_software_version;
wtpinfo->hardware_version=conf_hardware_version;
wtpinfo->bootloader_version=conf_bootloader_version;
wtpinfo->software_vendor_id=CW_VENDOR_ID_CISCO;
wtpinfo->macaddress=conf_macaddress;
@ -49,9 +54,12 @@ struct wtpinfo * get_wtpinfo()
wtpinfo->mac_type=0;
wtpinfo->session_id = malloc(8);
wtpinfo->session_id_len = cw_rand(wtpinfo->session_id,8);
// wtpinfo->session_id = malloc(8);
// wtpinfo->session_id_len = cw_rand(wtpinfo->session_id,8);
uint8_t sessid[4];
int sidl = cw_rand(sessid,4);
wtpinfo->session_id = bstr_create(sessid,sidl);
wtpinfo->frame_tunnel_mode=1;
return wtpinfo;

View File

@ -11,6 +11,7 @@
#include "capwap/cw_log.h"
#include "capwap/radioinfo.h"
#include "capwap/sock.h"
#include "capwap/capwap_ieee80211.h"
int wpa_printf()
{
@ -1207,13 +1208,18 @@ return 0;
int wtpdrv_get_num_radios()
{
return 4;
return 2;
}
/*
static int wtpdrv_get_radioinfo(int rid,struct radioinfo * radioinfo)
int wtpdrv_get_radioinfo(int rid,struct radioinfo * radioinfo)
{
radioinfo->rid=rid;
radioinfo->type|=CW_IEEE80211_RADIO_TYPE_B; //CWRADIO_TYPE_N;
/*
struct wpa_driver_ops * drv = wpa_drivers[0];
struct hostapd_hw_modes * hwm;
@ -1267,8 +1273,8 @@ static int wtpdrv_get_radioinfo(int rid,struct radioinfo * radioinfo)
// drv
*/
}
*/