4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
28 #include <sys/types.h>
29 #include <sys/stropts.h>
30 #include <sys/debug.h>
31 #include <sys/isa_defs.h>
32 #include <sys/dditypes.h>
33 #include <sys/ddi_impldefs.h>
34 #include "devid_impl.h"
36 static int devid_str_decode_id(char *devidstr
, ddi_devid_t
*devidp
,
37 char **minor_namep
, impl_devid_t
*id
);
45 ddi_devid_valid(ddi_devid_t devid
)
47 devid_valid(ddi_devid_t devid
)
50 impl_devid_t
*id
= (impl_devid_t
*)devid
;
53 DEVID_ASSERT(devid
!= NULL
);
55 if (id
->did_magic_hi
!= DEVID_MAGIC_MSB
)
56 return (DEVID_RET_INVALID
);
58 if (id
->did_magic_lo
!= DEVID_MAGIC_LSB
)
59 return (DEVID_RET_INVALID
);
61 if (id
->did_rev_hi
!= DEVID_REV_MSB
)
62 return (DEVID_RET_INVALID
);
64 if (id
->did_rev_lo
!= DEVID_REV_LSB
)
65 return (DEVID_RET_INVALID
);
67 type
= DEVID_GETTYPE(id
);
68 if ((type
== DEVID_NONE
) || (type
> DEVID_MAXTYPE
))
69 return (DEVID_RET_INVALID
);
71 return (DEVID_RET_VALID
);
75 * Return the sizeof a device id. If called with NULL devid it returns
76 * the amount of space needed to determine the size.
80 ddi_devid_sizeof(ddi_devid_t devid
)
82 devid_sizeof(ddi_devid_t devid
)
85 impl_devid_t
*id
= (impl_devid_t
*)devid
;
88 return (sizeof (*id
) - sizeof (id
->did_id
));
90 DEVID_ASSERT(DEVID_FUNC(devid_valid
)(devid
) == DEVID_RET_VALID
);
92 return (sizeof (*id
) + DEVID_GETLEN(id
) - sizeof (id
->did_id
));
96 * Compare two device id's.
103 ddi_devid_compare(ddi_devid_t id1
, ddi_devid_t id2
)
105 devid_compare(ddi_devid_t id1
, ddi_devid_t id2
)
109 impl_devid_t
*i_id1
= (impl_devid_t
*)id1
;
110 impl_devid_t
*i_id2
= (impl_devid_t
*)id2
;
114 DEVID_ASSERT((id1
!= NULL
) && (id2
!= NULL
));
115 DEVID_ASSERT(DEVID_FUNC(devid_valid
)(id1
) == DEVID_RET_VALID
);
116 DEVID_ASSERT(DEVID_FUNC(devid_valid
)(id2
) == DEVID_RET_VALID
);
118 /* magic and revision comparison */
119 if ((rval
= bcmp(id1
, id2
, 4)) != 0) {
123 /* get current devid types */
124 i_id1_type
= DEVID_GETTYPE(i_id1
);
125 i_id2_type
= DEVID_GETTYPE(i_id2
);
128 * Originaly all page83 devids used DEVID_SCSI3_WWN.
129 * To avoid a possible uniqueness issue each type of page83
130 * encoding supported is represented as a separate
131 * devid type. If comparing DEVID_SCSI3_WWN against
132 * one of the new page83 encodings we assume that no
133 * uniqueness issue exists (since we had apparently been
134 * running with the old DEVID_SCSI3_WWN encoding without
137 if ((i_id1_type
== DEVID_SCSI3_WWN
) ||
138 (i_id2_type
== DEVID_SCSI3_WWN
)) {
140 * Atleast one devid is using old scsi
141 * encode algorithm. Force devid types
142 * to same scheme for comparison.
144 if (IS_DEVID_SCSI3_VPD_TYPE(i_id1_type
)) {
145 i_id1_type
= DEVID_SCSI3_WWN
;
147 if (IS_DEVID_SCSI3_VPD_TYPE(i_id2_type
)) {
148 i_id2_type
= DEVID_SCSI3_WWN
;
152 /* type comparison */
153 if (i_id1_type
!= i_id2_type
) {
154 return ((i_id1_type
< i_id2_type
) ? -1 : 1);
157 /* length comparison */
158 if (DEVID_GETLEN(i_id1
) != DEVID_GETLEN(i_id2
)) {
159 return (DEVID_GETLEN(i_id1
) < DEVID_GETLEN(i_id2
) ? -1 : 1);
163 rval
= bcmp(i_id1
->did_id
, i_id2
->did_id
, DEVID_GETLEN(i_id1
));
173 ddi_devid_free(ddi_devid_t devid
)
175 devid_free(ddi_devid_t devid
)
178 DEVID_ASSERT(devid
!= NULL
);
179 DEVID_FREE(devid
, DEVID_FUNC(devid_sizeof
)(devid
));
183 * Encode a device id into a string. See ddi_impldefs.h for details.
187 ddi_devid_str_encode(ddi_devid_t devid
, char *minor_name
)
189 devid_str_encode(ddi_devid_t devid
, char *minor_name
)
192 impl_devid_t
*id
= (impl_devid_t
*)devid
;
193 size_t driver_len
, devid_len
, slen
;
194 char *sbuf
, *dsp
, *dp
, ta
;
197 /* "id0" is the encoded representation of a NULL device id */
199 if ((sbuf
= DEVID_MALLOC(4)) == NULL
)
201 *(sbuf
+0) = DEVID_MAGIC_MSB
;
202 *(sbuf
+1) = DEVID_MAGIC_LSB
;
209 if (DEVID_FUNC(devid_valid
)(devid
) != DEVID_RET_VALID
)
212 /* scan the driver hint to see how long the hint is */
213 for (driver_len
= 0; driver_len
< DEVID_HINT_SIZE
; driver_len
++)
214 if (id
->did_driver
[driver_len
] == '\0')
217 /* scan the contained did_id to see if it meets ascii requirements */
218 devid_len
= DEVID_GETLEN(id
);
219 for (ascii
= 1, i
= 0; i
< devid_len
; i
++)
220 if (!DEVID_IDBYTE_ISASCII(id
->did_id
[i
])) {
225 /* some types should always go hex even if they look ascii */
226 if (DEVID_TYPE_BIN_FORCEHEX(id
->did_type_lo
))
229 /* set the length of the resulting string */
230 slen
= 2 + 1; /* <magic><rev> "id1" */
231 slen
+= 1 + driver_len
+ 1 + 1; /* ",<driver>@<type>" */
232 slen
+= ascii
? devid_len
: (devid_len
* 2); /* did_id field */
235 slen
+= strlen(minor_name
); /* len of minor_name */
237 slen
+= 1; /* NULL */
239 /* allocate string */
240 if ((sbuf
= DEVID_MALLOC(slen
)) == NULL
)
243 /* perform encode of id to hex string */
245 *dsp
++ = id
->did_magic_hi
;
246 *dsp
++ = id
->did_magic_lo
;
247 *dsp
++ = DEVID_REV_BINTOASCII(id
->did_rev_lo
);
249 for (i
= 0; i
< driver_len
; i
++)
250 *dsp
++ = id
->did_driver
[i
];
252 ta
= DEVID_TYPE_BINTOASCII(id
->did_type_lo
);
254 ta
= DEVID_TYPE_SETASCII(ta
);
256 for (i
= 0, dp
= &id
->did_id
[0]; i
< devid_len
; i
++, dp
++) {
260 else if (*dp
== 0x00)
265 n
= ((*dp
) >> 4) & 0xF;
266 *dsp
++ = (n
< 10) ? (n
+ '0') : (n
+ ('a' - 10));
268 *dsp
++ = (n
< 10) ? (n
+ '0') : (n
+ ('a' - 10));
274 (void) strcpy(dsp
, minor_name
);
278 /* ensure that (strlen + 1) is correct length for free */
279 DEVID_ASSERT((strlen(sbuf
) + 1) == slen
);
283 /* free the string returned by devid_str_encode */
286 ddi_devid_str_free(char *devidstr
)
288 devid_str_free(char *devidstr
)
291 DEVID_FREE(devidstr
, strlen(devidstr
) + 1);
295 * given the string representation of a device id returned by calling
296 * devid_str_encode (passed in as devidstr), return pointers to the
297 * broken out devid and minor_name as requested. Devidstr remains
298 * allocated and unmodified. The devid returned in *devidp should be freed by
299 * calling devid_free. The minor_name returned in minor_namep should
300 * be freed by calling devid_str_free(minor_namep).
302 * See ddi_impldefs.h for format details.
306 ddi_devid_str_decode(
310 char *devidstr
, ddi_devid_t
*devidp
, char **minor_namep
)
312 return (devid_str_decode_id(devidstr
, devidp
, minor_namep
, NULL
));
315 /* implementation for (ddi_)devid_str_decode */
317 devid_str_decode_id(char *devidstr
, ddi_devid_t
*devidp
,
318 char **minor_namep
, impl_devid_t
*id
)
320 char *str
, *msp
, *dsp
, *dp
, ta
;
321 int slen
, devid_len
, ascii
, i
, n
, c
, pre_alloc
= FALSE
;
322 unsigned short id_len
, type
; /* for hibyte/lobyte */
326 if (minor_namep
!= NULL
)
331 if (devidstr
== NULL
)
332 return (DEVID_FAILURE
);
334 /* the string must atleast contain the ascii two byte header */
335 slen
= strlen(devidstr
);
336 if ((slen
< 3) || (devidstr
[0] != DEVID_MAGIC_MSB
) ||
337 (devidstr
[1] != DEVID_MAGIC_LSB
))
338 return (DEVID_FAILURE
);
340 /* "id0" is the encoded representation of a NULL device id */
341 if ((devidstr
[2] == '0') && (slen
== 3))
342 return (DEVID_SUCCESS
);
344 /* "id1,@S0" is the shortest possible, reject if shorter */
346 return (DEVID_FAILURE
);
348 /* find the optional minor name, start after ',' */
349 if ((msp
= strchr(&devidstr
[4], '/')) != NULL
)
352 /* skip devid processing if we are not asked to return it */
354 /* find the required '@' separator */
355 if ((str
= strchr(devidstr
, '@')) == NULL
)
356 return (DEVID_FAILURE
);
357 str
++; /* skip '@' */
359 /* pick up <type> after the '@' and verify */
361 ascii
= DEVID_TYPE_ISASCII(ta
);
362 type
= DEVID_TYPE_ASCIITOBIN(ta
);
363 if (type
> DEVID_MAXTYPE
)
364 return (DEVID_FAILURE
);
366 /* determine length of id->did_id field */
368 id_len
= strlen(str
);
370 id_len
= msp
- str
- 1;
372 /* account for encoding: with hex, binary is half the size */
374 /* hex id field must be even length */
376 return (DEVID_FAILURE
);
380 /* add in size of the binary devid header */
381 devid_len
= id_len
+ sizeof (*id
) - sizeof (id
->did_id
);
384 * Allocate space for devid if we are asked to decode it
385 * decode it and space wasn't pre-allocated.
387 if (pre_alloc
== FALSE
) {
388 if ((id
= (impl_devid_t
*)DEVID_MALLOC(
390 return (DEVID_FAILURE
);
393 /* decode header portion of the string into the binary devid */
395 id
->did_magic_hi
= *dsp
++; /* <magic> "id" */
396 id
->did_magic_lo
= *dsp
++;
399 DEVID_REV_ASCIITOBIN(*dsp
); /* <rev> "1" */
400 dsp
++; /* skip "1" */
401 dsp
++; /* skip "," */
402 for (i
= 0; i
< DEVID_HINT_SIZE
; i
++) { /* <driver>@ */
405 id
->did_driver
[i
] = *dsp
++;
407 for (; i
< DEVID_HINT_SIZE
; i
++)
408 id
->did_driver
[i
] = 0;
410 /* we must now be at the '@' */
414 /* set the type and length */
415 DEVID_FORMTYPE(id
, type
);
416 DEVID_FORMLEN(id
, id_len
);
418 /* decode devid portion of string into the binary */
419 for (i
= 0, dsp
= str
, dp
= &id
->did_id
[0];
420 i
< id_len
; i
++, dp
++) {
424 else if (*dsp
== '~')
431 if (c
>= '0' && c
<= '9')
432 n
= (c
- '0') & 0xFF;
433 else if (c
>= 'a' && c
<= 'f')
434 n
= (c
- ('a' - 10)) & 0xFF;
439 if (c
>= '0' && c
<= '9')
440 n
|= (c
- '0') & 0xFF;
441 else if (c
>= 'a' && c
<= 'f')
442 n
|= (c
- ('a' - 10)) & 0xFF;
450 if (DEVID_FUNC(devid_valid
)((ddi_devid_t
)id
) != DEVID_RET_VALID
)
454 /* duplicate minor_name if we are asked to decode it */
455 if (minor_namep
&& msp
) {
456 if ((*minor_namep
= DEVID_MALLOC(strlen(msp
) + 1)) == NULL
)
458 (void) strcpy(*minor_namep
, msp
);
461 /* return pointer to binary */
463 *devidp
= (ddi_devid_t
)id
;
464 return (DEVID_SUCCESS
);
467 if ((pre_alloc
== FALSE
) && (id
))
468 DEVID_FREE(id
, devid_len
);
469 return (DEVID_FAILURE
);
474 * Compare two device id's in string form
475 * -1 - id1 less than id2
477 * 1 - id1 greater than id2
481 ddi_devid_str_compare(char *id1_str
, char *id2_str
)
483 devid_str_compare(char *id1_str
, char *id2_str
)
486 int rval
= DEVID_FAILURE
;
490 /* kernel use static protected by lock. */
491 static kmutex_t id_lock
;
492 static uchar_t id1
[sizeof (impl_devid_t
) + MAXPATHLEN
];
493 static uchar_t id2
[sizeof (impl_devid_t
) + MAXPATHLEN
];
495 /* userland place on stack, since malloc might fail */
496 uchar_t id1
[sizeof (impl_devid_t
) + MAXPATHLEN
];
497 uchar_t id2
[sizeof (impl_devid_t
) + MAXPATHLEN
];
501 mutex_enter(&id_lock
);
505 * encode string form of devid
507 if ((devid_str_decode_id(id1_str
, &devid1
, NULL
, (impl_devid_t
*)id1
) ==
509 (devid_str_decode_id(id2_str
, &devid2
, NULL
, (impl_devid_t
*)id2
) ==
511 rval
= DEVID_FUNC(devid_compare
)(devid1
, devid2
);
515 mutex_exit(&id_lock
);