| @@ -1,259 +1,262 @@ | | | @@ -1,259 +1,262 @@ |
1 | /* $NetBSD: ata_raid_via.c,v 1.5 2008/03/18 20:46:36 cube Exp $ */ | | 1 | /* $NetBSD: ata_raid_via.c,v 1.6 2008/09/19 16:49:27 christos Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2000,2001,2002 Søren Schmidt <sos@FreeBSD.org> | | 4 | * Copyright (c) 2000,2001,2002 Søren Schmidt <sos@FreeBSD.org> |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer, | | 11 | * notice, this list of conditions and the following disclaimer, |
12 | * without modification, immediately at the beginning of the file. | | 12 | * without modification, immediately at the beginning of the file. |
13 | * 2. Redistributions in binary form must reproduce the above copyright | | 13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the | | 14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. | | 15 | * documentation and/or other materials provided with the distribution. |
16 | * 3. The name of the author may not be used to endorse or promote products | | 16 | * 3. The name of the author may not be used to endorse or promote products |
17 | * derived from this software without specific prior written permission. | | 17 | * derived from this software without specific prior written permission. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | | 19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | | 20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | 21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
22 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | | 22 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | | 23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
24 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | | 24 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | | 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | | 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | | 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | | 28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | */ | | 29 | */ |
30 | | | 30 | |
31 | /* | | 31 | /* |
32 | * Support for parsing VIA V-RAID ATA RAID controller configuration blocks. | | 32 | * Support for parsing VIA V-RAID ATA RAID controller configuration blocks. |
33 | * | | 33 | * |
34 | * Adapted to NetBSD by Tim Rightnour (garbled@netbsd.org) | | 34 | * Adapted to NetBSD by Tim Rightnour (garbled@netbsd.org) |
35 | */ | | 35 | */ |
36 | | | 36 | |
37 | #include <sys/cdefs.h> | | 37 | #include <sys/cdefs.h> |
38 | __KERNEL_RCSID(0, "$NetBSD: ata_raid_via.c,v 1.5 2008/03/18 20:46:36 cube Exp $"); | | 38 | __KERNEL_RCSID(0, "$NetBSD: ata_raid_via.c,v 1.6 2008/09/19 16:49:27 christos Exp $"); |
39 | | | 39 | |
40 | #include <sys/param.h> | | 40 | #include <sys/param.h> |
41 | #include <sys/buf.h> | | 41 | #include <sys/buf.h> |
42 | #include <sys/bufq.h> | | 42 | #include <sys/bufq.h> |
43 | #include <sys/conf.h> | | 43 | #include <sys/conf.h> |
44 | #include <sys/device.h> | | 44 | #include <sys/device.h> |
45 | #include <sys/disk.h> | | 45 | #include <sys/disk.h> |
46 | #include <sys/disklabel.h> | | 46 | #include <sys/disklabel.h> |
47 | #include <sys/fcntl.h> | | 47 | #include <sys/fcntl.h> |
48 | #include <sys/malloc.h> | | 48 | #include <sys/malloc.h> |
49 | #include <sys/vnode.h> | | 49 | #include <sys/vnode.h> |
50 | #include <sys/kauth.h> | | 50 | #include <sys/kauth.h> |
51 | | | 51 | |
52 | #include <miscfs/specfs/specdev.h> | | 52 | #include <miscfs/specfs/specdev.h> |
53 | | | 53 | |
54 | #include <dev/ata/atareg.h> | | 54 | #include <dev/ata/atareg.h> |
55 | #include <dev/ata/atavar.h> | | 55 | #include <dev/ata/atavar.h> |
56 | #include <dev/ata/wdvar.h> | | 56 | #include <dev/ata/wdvar.h> |
57 | | | 57 | |
58 | #include <dev/ata/ata_raidreg.h> | | 58 | #include <dev/ata/ata_raidreg.h> |
59 | #include <dev/ata/ata_raidvar.h> | | 59 | #include <dev/ata/ata_raidvar.h> |
60 | | | 60 | |
61 | #ifdef ATA_RAID_DEBUG | | 61 | #ifdef ATA_RAID_DEBUG |
62 | #define DPRINTF(x) printf x | | 62 | #define DPRINTF(x) printf x |
63 | #else | | 63 | #else |
64 | #define DPRINTF(x) /* nothing */ | | 64 | #define DPRINTF(x) /* nothing */ |
65 | #endif | | 65 | #endif |
66 | | | 66 | |
67 | #ifdef ATA_RAID_DEBUG | | 67 | #ifdef ATA_RAID_DEBUG |
68 | static const char * | | 68 | static const char * |
69 | ata_raid_via_type(int type) | | 69 | ata_raid_via_type(int type) |
70 | { | | 70 | { |
71 | static char buffer[16]; | | 71 | static char buffer[16]; |
72 | | | 72 | |
73 | switch (type) { | | 73 | switch (type) { |
74 | case VIA_T_RAID0: return "RAID0"; | | 74 | case VIA_T_RAID0: return "RAID0"; |
75 | case VIA_T_RAID1: return "RAID1"; | | 75 | case VIA_T_RAID1: return "RAID1"; |
76 | case VIA_T_RAID5: return "RAID5"; | | 76 | case VIA_T_RAID5: return "RAID5"; |
77 | case VIA_T_RAID01: return "RAID0+1"; | | 77 | case VIA_T_RAID01: return "RAID0+1"; |
78 | case VIA_T_SPAN: return "SPAN"; | | 78 | case VIA_T_SPAN: return "SPAN"; |
79 | default: | | 79 | default: |
80 | sprintf(buffer, "UNKNOWN 0x%02x", type); | | 80 | sprintf(buffer, "UNKNOWN 0x%02x", type); |
81 | return buffer; | | 81 | return buffer; |
82 | } | | 82 | } |
83 | } | | 83 | } |
84 | | | 84 | |
85 | static void | | 85 | static void |
86 | ata_raid_via_print_info(struct via_raid_conf *info) | | 86 | ata_raid_via_print_info(struct via_raid_conf *info) |
87 | { | | 87 | { |
88 | int i; | | 88 | int i; |
89 | | | 89 | |
90 | printf("*************** ATA VIA Metadata ****************\n"); | | 90 | printf("*************** ATA VIA Metadata ****************\n"); |
91 | printf("magic 0x%02x\n", info->magic); | | 91 | printf("magic 0x%02x\n", info->magic); |
92 | printf("dummy_0 0x%02x\n", info->dummy_0); | | 92 | printf("dummy_0 0x%02x\n", info->dummy_0); |
93 | printf("type %s\n", | | 93 | printf("type %s\n", |
94 | ata_raid_via_type(info->type & VIA_T_MASK)); | | 94 | ata_raid_via_type(info->type & VIA_T_MASK)); |
95 | printf("bootable %d\n", info->type & VIA_T_BOOTABLE); | | 95 | printf("bootable %d\n", info->type & VIA_T_BOOTABLE); |
96 | printf("unknown %d\n", info->type & VIA_T_UNKNOWN); | | 96 | printf("unknown %d\n", info->type & VIA_T_UNKNOWN); |
97 | printf("disk_index 0x%02x\n", info->disk_index); | | 97 | printf("disk_index 0x%02x\n", info->disk_index); |
98 | printf("stripe_layout 0x%02x\n", info->stripe_layout); | | 98 | printf("stripe_layout 0x%02x\n", info->stripe_layout); |
99 | printf(" stripe_disks %d\n", info->stripe_layout & VIA_L_DISKS); | | 99 | printf(" stripe_disks %d\n", info->stripe_layout & VIA_L_DISKS); |
100 | printf(" stripe_sectors %d\n", | | 100 | printf(" stripe_sectors %d\n", |
101 | 0x08 << ((info->stripe_layout & VIA_L_MASK) >> VIA_L_SHIFT)); | | 101 | 0x08 << ((info->stripe_layout & VIA_L_MASK) >> VIA_L_SHIFT)); |
102 | printf("disk_sectors %ju\n", info->disk_sectors); | | 102 | printf("disk_sectors %ju\n", info->disk_sectors); |
103 | printf("disk_id 0x%08x\n", info->disk_id); | | 103 | printf("disk_id 0x%08x\n", info->disk_id); |
104 | printf("DISK# disk_id\n"); | | 104 | printf("DISK# disk_id\n"); |
105 | for (i = 0; i < 8; i++) { | | 105 | for (i = 0; i < 8; i++) { |
106 | if (info->disks[i]) | | 106 | if (info->disks[i]) |
107 | printf(" %d 0x%08x\n", i, info->disks[i]); | | 107 | printf(" %d 0x%08x\n", i, info->disks[i]); |
108 | } | | 108 | } |
109 | printf("checksum 0x%02x\n", info->checksum); | | 109 | printf("checksum 0x%02x\n", info->checksum); |
110 | printf("=================================================\n"); | | 110 | printf("=================================================\n"); |
111 | } | | 111 | } |
112 | #endif | | 112 | #endif |
113 | | | 113 | |
114 | int | | 114 | int |
115 | ata_raid_read_config_via(struct wd_softc *sc) | | 115 | ata_raid_read_config_via(struct wd_softc *sc) |
116 | { | | 116 | { |
117 | struct via_raid_conf *info; | | 117 | struct via_raid_conf *info; |
118 | struct atabus_softc *atabus; | | 118 | struct atabus_softc *atabus; |
119 | struct vnode *vp; | | 119 | struct vnode *vp; |
120 | int bmajor, error; | | 120 | int bmajor, error; |
121 | dev_t dev; | | 121 | dev_t dev; |
122 | uint32_t drive; | | 122 | uint32_t drive; |
123 | uint8_t checksum, *ptr; | | 123 | uint8_t checksum, checksum_alt, byte3, *ptr; |
124 | int count, disk; | | 124 | int count, disk; |
125 | struct ataraid_array_info *aai; | | 125 | struct ataraid_array_info *aai; |
126 | struct ataraid_disk_info *adi; | | 126 | struct ataraid_disk_info *adi; |
127 | | | 127 | |
128 | info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK); | | 128 | info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK); |
129 | | | 129 | |
130 | bmajor = devsw_name2blk(device_xname(sc->sc_dev), NULL, 0); | | 130 | bmajor = devsw_name2blk(device_xname(sc->sc_dev), NULL, 0); |
131 | | | 131 | |
132 | /* Get a vnode for the raw partition of this disk. */ | | 132 | /* Get a vnode for the raw partition of this disk. */ |
133 | dev = MAKEDISKDEV(bmajor, device_unit(sc->sc_dev), RAW_PART); | | 133 | dev = MAKEDISKDEV(bmajor, device_unit(sc->sc_dev), RAW_PART); |
134 | error = bdevvp(dev, &vp); | | 134 | error = bdevvp(dev, &vp); |
135 | if (error) | | 135 | if (error) |
136 | goto out; | | 136 | goto out; |
137 | | | 137 | |
138 | error = VOP_OPEN(vp, FREAD, NOCRED); | | 138 | error = VOP_OPEN(vp, FREAD, NOCRED); |
139 | if (error) { | | 139 | if (error) { |
140 | vput(vp); | | 140 | vput(vp); |
141 | goto out; | | 141 | goto out; |
142 | } | | 142 | } |
143 | | | 143 | |
144 | error = ata_raid_config_block_rw(vp, VIA_LBA(sc), info, | | 144 | error = ata_raid_config_block_rw(vp, VIA_LBA(sc), info, |
145 | sizeof(*info), B_READ); | | 145 | sizeof(*info), B_READ); |
146 | VOP_CLOSE(vp, FREAD, NOCRED); | | 146 | VOP_CLOSE(vp, FREAD, NOCRED); |
147 | vput(vp); | | 147 | vput(vp); |
148 | if (error) { | | 148 | if (error) { |
149 | aprint_error_dev(sc->sc_dev, | | 149 | aprint_error_dev(sc->sc_dev, |
150 | "error %d reading VIA V-RAID config block\n", error); | | 150 | "error %d reading VIA V-RAID config block\n", error); |
151 | goto out; | | 151 | goto out; |
152 | } | | 152 | } |
153 | | | 153 | |
154 | #ifdef ATA_RAID_DEBUG | | 154 | #ifdef ATA_RAID_DEBUG |
155 | ata_raid_via_print_info(info); | | 155 | ata_raid_via_print_info(info); |
156 | printf("MAGIC == 0x%02x\n", info->magic); | | 156 | printf("MAGIC == 0x%02x\n", info->magic); |
157 | #endif | | 157 | #endif |
158 | | | 158 | |
159 | /* Check the signature. */ | | 159 | /* Check the signature. */ |
160 | if (info->magic != VIA_MAGIC) { | | 160 | if (info->magic != VIA_MAGIC) { |
161 | DPRINTF(("%s: VIA V-RAID signature check failed\n", | | 161 | DPRINTF(("%s: VIA V-RAID signature check failed\n", |
162 | device_xname(sc->sc_dev))); | | 162 | device_xname(sc->sc_dev))); |
163 | error = ESRCH; | | 163 | error = ESRCH; |
164 | goto out; | | 164 | goto out; |
165 | } | | 165 | } |
166 | | | 166 | |
167 | /* calculate checksum and compare for valid */ | | 167 | /* calculate checksum and compare for valid */ |
168 | for (checksum = 0, ptr = (uint8_t *)info, count = 0; count < 50; | | 168 | for (byte3 = 0, checksum = 0, ptr = (uint8_t *)info, count = 0; |
169 | count++) | | 169 | count < 50; count++) |
170 | if (count == 3) | | 170 | if (count == 3) |
171 | checksum += *ptr++ & ~ VIA_T_BOOTABLE; | | 171 | byte3 = *ptr++; |
172 | else | | 172 | else |
173 | checksum += *ptr++; | | 173 | checksum += *ptr++; |
174 | if (checksum != info->checksum) { | | 174 | checksum_alt = checksum + (byte3 & ~VIA_T_BOOTABLE); |
175 | DPRINTF(("%s: VIA V-RAID checksum failed 0x%02x != 0x%02x\n", | | 175 | checksum += byte3; |
176 | device_xname(sc->sc_dev), checksum, info->checksum)); | | 176 | if (checksum != info->checksum && checksum_alt != info->checksum) { |
| | | 177 | DPRINTF(("%s: VIA V-RAID checksum failed 0x%02x != " |
| | | 178 | "0x%02x or 0x%02x\n", device_xname(sc->sc_dev), |
| | | 179 | info->checksum, checksum, checksum_alt)); |
177 | error = ESRCH; | | 180 | error = ESRCH; |
178 | goto out; | | 181 | goto out; |
179 | } | | 182 | } |
180 | | | 183 | |
181 | /* | | 184 | /* |
182 | * Lookup or allocate a new array info structure for | | 185 | * Lookup or allocate a new array info structure for |
183 | * this array. Use the serial number of disk0 as the array# | | 186 | * this array. Use the serial number of disk0 as the array# |
184 | */ | | 187 | */ |
185 | aai = ata_raid_get_array_info(ATA_RAID_TYPE_VIA, info->disks[0]); | | 188 | aai = ata_raid_get_array_info(ATA_RAID_TYPE_VIA, info->disks[0]); |
186 | | | 189 | |
187 | aai->aai_status = AAI_S_READY; | | 190 | aai->aai_status = AAI_S_READY; |
188 | | | 191 | |
189 | switch (info->type & VIA_T_MASK) { | | 192 | switch (info->type & VIA_T_MASK) { |
190 | case VIA_T_RAID0: | | 193 | case VIA_T_RAID0: |
191 | aai->aai_level = AAI_L_RAID0; | | 194 | aai->aai_level = AAI_L_RAID0; |
192 | aai->aai_width = info->stripe_layout & VIA_L_DISKS; | | 195 | aai->aai_width = info->stripe_layout & VIA_L_DISKS; |
193 | aai->aai_capacity = aai->aai_width * info->disk_sectors; | | 196 | aai->aai_capacity = aai->aai_width * info->disk_sectors; |
194 | break; | | 197 | break; |
195 | | | 198 | |
196 | case VIA_T_RAID1: | | 199 | case VIA_T_RAID1: |
197 | aai->aai_level = AAI_L_RAID1; | | 200 | aai->aai_level = AAI_L_RAID1; |
198 | aai->aai_width = 1; | | 201 | aai->aai_width = 1; |
199 | aai->aai_capacity = aai->aai_width * info->disk_sectors; | | 202 | aai->aai_capacity = aai->aai_width * info->disk_sectors; |
200 | break; | | 203 | break; |
201 | | | 204 | |
202 | case VIA_T_RAID5: | | 205 | case VIA_T_RAID5: |
203 | aai->aai_level = AAI_L_RAID5; | | 206 | aai->aai_level = AAI_L_RAID5; |
204 | aai->aai_width = info->stripe_layout & VIA_L_DISKS; | | 207 | aai->aai_width = info->stripe_layout & VIA_L_DISKS; |
205 | aai->aai_capacity = (aai->aai_width - 1) * info->disk_sectors; | | 208 | aai->aai_capacity = (aai->aai_width - 1) * info->disk_sectors; |
206 | break; | | 209 | break; |
207 | | | 210 | |
208 | case VIA_T_SPAN: | | 211 | case VIA_T_SPAN: |
209 | aai->aai_level = AAI_L_SPAN; | | 212 | aai->aai_level = AAI_L_SPAN; |
210 | aai->aai_width = 1; | | 213 | aai->aai_width = 1; |
211 | aai->aai_capacity += info->disk_sectors; /* XXX ??? */ | | 214 | aai->aai_capacity += info->disk_sectors; /* XXX ??? */ |
212 | break; | | 215 | break; |
213 | | | 216 | |
214 | default: | | 217 | default: |
215 | aprint_error_dev(sc->sc_dev, | | 218 | aprint_error_dev(sc->sc_dev, |
216 | "unknown VIA V-RAID type 0x%02x\n", info->type); | | 219 | "unknown VIA V-RAID type 0x%02x\n", info->type); |
217 | error = EINVAL; | | 220 | error = EINVAL; |
218 | goto out; | | 221 | goto out; |
219 | } | | 222 | } |
220 | | | 223 | |
221 | aai->aai_type = ATA_RAID_TYPE_VIA; | | 224 | aai->aai_type = ATA_RAID_TYPE_VIA; |
222 | for (count = 0, disk = 0; disk < 8; disk++) | | 225 | for (count = 0, disk = 0; disk < 8; disk++) |
223 | if (info->disks[disk]) | | 226 | if (info->disks[disk]) |
224 | count++; | | 227 | count++; |
225 | aai->aai_interleave = | | 228 | aai->aai_interleave = |
226 | 0x08 << ((info->stripe_layout & VIA_L_MASK) >> VIA_L_SHIFT); | | 229 | 0x08 << ((info->stripe_layout & VIA_L_MASK) >> VIA_L_SHIFT); |
227 | aai->aai_ndisks = count; | | 230 | aai->aai_ndisks = count; |
228 | aai->aai_heads = 255; | | 231 | aai->aai_heads = 255; |
229 | aai->aai_sectors = 63; | | 232 | aai->aai_sectors = 63; |
230 | aai->aai_cylinders = aai->aai_capacity / (63 * 255); | | 233 | aai->aai_cylinders = aai->aai_capacity / (63 * 255); |
231 | aai->aai_offset = 0; | | 234 | aai->aai_offset = 0; |
232 | aai->aai_reserved = 1; | | 235 | aai->aai_reserved = 1; |
233 | | | 236 | |
234 | /* XXX - bogus. RAID1 shouldn't really have an interleave */ | | 237 | /* XXX - bogus. RAID1 shouldn't really have an interleave */ |
235 | if (aai->aai_interleave == 0) | | 238 | if (aai->aai_interleave == 0) |
236 | aai->aai_interleave = aai->aai_capacity; | | 239 | aai->aai_interleave = aai->aai_capacity; |
237 | | | 240 | |
238 | atabus = device_private(device_parent(sc->sc_dev)); | | 241 | atabus = device_private(device_parent(sc->sc_dev)); |
239 | drive = atabus->sc_chan->ch_channel; | | 242 | drive = atabus->sc_chan->ch_channel; |
240 | if (drive >= aai->aai_ndisks) { | | 243 | if (drive >= aai->aai_ndisks) { |
241 | aprint_error_dev(sc->sc_dev, | | 244 | aprint_error_dev(sc->sc_dev, |
242 | "drive number %d doesn't make sense within %d-disk " | | 245 | "drive number %d doesn't make sense within %d-disk " |
243 | "array\n", drive, aai->aai_ndisks); | | 246 | "array\n", drive, aai->aai_ndisks); |
244 | error = EINVAL; | | 247 | error = EINVAL; |
245 | goto out; | | 248 | goto out; |
246 | } | | 249 | } |
247 | | | 250 | |
248 | adi = &aai->aai_disks[drive]; | | 251 | adi = &aai->aai_disks[drive]; |
249 | adi->adi_dev = sc->sc_dev; | | 252 | adi->adi_dev = sc->sc_dev; |
250 | adi->adi_status = ADI_S_ONLINE | ADI_S_ASSIGNED; | | 253 | adi->adi_status = ADI_S_ONLINE | ADI_S_ASSIGNED; |
251 | adi->adi_sectors = aai->aai_capacity; | | 254 | adi->adi_sectors = aai->aai_capacity; |
252 | adi->adi_compsize = info->disk_sectors; | | 255 | adi->adi_compsize = info->disk_sectors; |
253 | | | 256 | |
254 | error = 0; | | 257 | error = 0; |
255 | | | 258 | |
256 | out: | | 259 | out: |
257 | free(info, M_DEVBUF); | | 260 | free(info, M_DEVBUF); |
258 | return (error); | | 261 | return (error); |
259 | } | | 262 | } |