diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/aic7xxx/aic7xxx_proc.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/aic7xxx/aic7xxx_proc.c')
-rw-r--r-- | drivers/scsi/aic7xxx/aic7xxx_proc.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/drivers/scsi/aic7xxx/aic7xxx_proc.c b/drivers/scsi/aic7xxx/aic7xxx_proc.c new file mode 100644 index 000000000000..85e80eecc9d0 --- /dev/null +++ b/drivers/scsi/aic7xxx/aic7xxx_proc.c | |||
@@ -0,0 +1,385 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2001 Adaptec 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 | * without modification. | ||
11 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer | ||
12 | * substantially similar to the "NO WARRANTY" disclaimer below | ||
13 | * ("Disclaimer") and any redistribution must be conditioned upon | ||
14 | * including a substantially similar Disclaimer requirement for further | ||
15 | * binary redistribution. | ||
16 | * 3. Neither the names of the above-listed copyright holders nor the names | ||
17 | * of any contributors may be used to endorse or promote products derived | ||
18 | * from this software without specific prior written permission. | ||
19 | * | ||
20 | * Alternatively, this software may be distributed under the terms of the | ||
21 | * GNU General Public License ("GPL") version 2 as published by the Free | ||
22 | * Software Foundation. | ||
23 | * | ||
24 | * NO WARRANTY | ||
25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
26 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
27 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR | ||
28 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
29 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
30 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
31 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
32 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
33 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | ||
34 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
35 | * POSSIBILITY OF SUCH DAMAGES. | ||
36 | * | ||
37 | * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr> | ||
38 | * sym driver. | ||
39 | * | ||
40 | * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_proc.c#29 $ | ||
41 | */ | ||
42 | #include "aic7xxx_osm.h" | ||
43 | #include "aic7xxx_inline.h" | ||
44 | #include "aic7xxx_93cx6.h" | ||
45 | |||
46 | static void copy_mem_info(struct info_str *info, char *data, int len); | ||
47 | static int copy_info(struct info_str *info, char *fmt, ...); | ||
48 | static void ahc_dump_target_state(struct ahc_softc *ahc, | ||
49 | struct info_str *info, | ||
50 | u_int our_id, char channel, | ||
51 | u_int target_id, u_int target_offset); | ||
52 | static void ahc_dump_device_state(struct info_str *info, | ||
53 | struct ahc_linux_device *dev); | ||
54 | static int ahc_proc_write_seeprom(struct ahc_softc *ahc, | ||
55 | char *buffer, int length); | ||
56 | |||
57 | static void | ||
58 | copy_mem_info(struct info_str *info, char *data, int len) | ||
59 | { | ||
60 | if (info->pos + len > info->offset + info->length) | ||
61 | len = info->offset + info->length - info->pos; | ||
62 | |||
63 | if (info->pos + len < info->offset) { | ||
64 | info->pos += len; | ||
65 | return; | ||
66 | } | ||
67 | |||
68 | if (info->pos < info->offset) { | ||
69 | off_t partial; | ||
70 | |||
71 | partial = info->offset - info->pos; | ||
72 | data += partial; | ||
73 | info->pos += partial; | ||
74 | len -= partial; | ||
75 | } | ||
76 | |||
77 | if (len > 0) { | ||
78 | memcpy(info->buffer, data, len); | ||
79 | info->pos += len; | ||
80 | info->buffer += len; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | static int | ||
85 | copy_info(struct info_str *info, char *fmt, ...) | ||
86 | { | ||
87 | va_list args; | ||
88 | char buf[256]; | ||
89 | int len; | ||
90 | |||
91 | va_start(args, fmt); | ||
92 | len = vsprintf(buf, fmt, args); | ||
93 | va_end(args); | ||
94 | |||
95 | copy_mem_info(info, buf, len); | ||
96 | return (len); | ||
97 | } | ||
98 | |||
99 | void | ||
100 | ahc_format_transinfo(struct info_str *info, struct ahc_transinfo *tinfo) | ||
101 | { | ||
102 | u_int speed; | ||
103 | u_int freq; | ||
104 | u_int mb; | ||
105 | |||
106 | speed = 3300; | ||
107 | freq = 0; | ||
108 | if (tinfo->offset != 0) { | ||
109 | freq = aic_calc_syncsrate(tinfo->period); | ||
110 | speed = freq; | ||
111 | } | ||
112 | speed *= (0x01 << tinfo->width); | ||
113 | mb = speed / 1000; | ||
114 | if (mb > 0) | ||
115 | copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000); | ||
116 | else | ||
117 | copy_info(info, "%dKB/s transfers", speed); | ||
118 | |||
119 | if (freq != 0) { | ||
120 | copy_info(info, " (%d.%03dMHz%s, offset %d", | ||
121 | freq / 1000, freq % 1000, | ||
122 | (tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0 | ||
123 | ? " DT" : "", tinfo->offset); | ||
124 | } | ||
125 | |||
126 | if (tinfo->width > 0) { | ||
127 | if (freq != 0) { | ||
128 | copy_info(info, ", "); | ||
129 | } else { | ||
130 | copy_info(info, " ("); | ||
131 | } | ||
132 | copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width)); | ||
133 | } else if (freq != 0) { | ||
134 | copy_info(info, ")"); | ||
135 | } | ||
136 | copy_info(info, "\n"); | ||
137 | } | ||
138 | |||
139 | static void | ||
140 | ahc_dump_target_state(struct ahc_softc *ahc, struct info_str *info, | ||
141 | u_int our_id, char channel, u_int target_id, | ||
142 | u_int target_offset) | ||
143 | { | ||
144 | struct ahc_linux_target *targ; | ||
145 | struct ahc_initiator_tinfo *tinfo; | ||
146 | struct ahc_tmode_tstate *tstate; | ||
147 | int lun; | ||
148 | |||
149 | tinfo = ahc_fetch_transinfo(ahc, channel, our_id, | ||
150 | target_id, &tstate); | ||
151 | if ((ahc->features & AHC_TWIN) != 0) | ||
152 | copy_info(info, "Channel %c ", channel); | ||
153 | copy_info(info, "Target %d Negotiation Settings\n", target_id); | ||
154 | copy_info(info, "\tUser: "); | ||
155 | ahc_format_transinfo(info, &tinfo->user); | ||
156 | targ = ahc->platform_data->targets[target_offset]; | ||
157 | if (targ == NULL) | ||
158 | return; | ||
159 | |||
160 | copy_info(info, "\tGoal: "); | ||
161 | ahc_format_transinfo(info, &tinfo->goal); | ||
162 | copy_info(info, "\tCurr: "); | ||
163 | ahc_format_transinfo(info, &tinfo->curr); | ||
164 | |||
165 | for (lun = 0; lun < AHC_NUM_LUNS; lun++) { | ||
166 | struct ahc_linux_device *dev; | ||
167 | |||
168 | dev = targ->devices[lun]; | ||
169 | |||
170 | if (dev == NULL) | ||
171 | continue; | ||
172 | |||
173 | ahc_dump_device_state(info, dev); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | static void | ||
178 | ahc_dump_device_state(struct info_str *info, struct ahc_linux_device *dev) | ||
179 | { | ||
180 | copy_info(info, "\tChannel %c Target %d Lun %d Settings\n", | ||
181 | dev->target->channel + 'A', dev->target->target, dev->lun); | ||
182 | |||
183 | copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued); | ||
184 | copy_info(info, "\t\tCommands Active %d\n", dev->active); | ||
185 | copy_info(info, "\t\tCommand Openings %d\n", dev->openings); | ||
186 | copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags); | ||
187 | copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen); | ||
188 | } | ||
189 | |||
190 | static int | ||
191 | ahc_proc_write_seeprom(struct ahc_softc *ahc, char *buffer, int length) | ||
192 | { | ||
193 | struct seeprom_descriptor sd; | ||
194 | int have_seeprom; | ||
195 | u_long s; | ||
196 | int paused; | ||
197 | int written; | ||
198 | |||
199 | /* Default to failure. */ | ||
200 | written = -EINVAL; | ||
201 | ahc_lock(ahc, &s); | ||
202 | paused = ahc_is_paused(ahc); | ||
203 | if (!paused) | ||
204 | ahc_pause(ahc); | ||
205 | |||
206 | if (length != sizeof(struct seeprom_config)) { | ||
207 | printf("ahc_proc_write_seeprom: incorrect buffer size\n"); | ||
208 | goto done; | ||
209 | } | ||
210 | |||
211 | have_seeprom = ahc_verify_cksum((struct seeprom_config*)buffer); | ||
212 | if (have_seeprom == 0) { | ||
213 | printf("ahc_proc_write_seeprom: cksum verification failed\n"); | ||
214 | goto done; | ||
215 | } | ||
216 | |||
217 | sd.sd_ahc = ahc; | ||
218 | #if AHC_PCI_CONFIG > 0 | ||
219 | if ((ahc->chip & AHC_PCI) != 0) { | ||
220 | sd.sd_control_offset = SEECTL; | ||
221 | sd.sd_status_offset = SEECTL; | ||
222 | sd.sd_dataout_offset = SEECTL; | ||
223 | if (ahc->flags & AHC_LARGE_SEEPROM) | ||
224 | sd.sd_chip = C56_66; | ||
225 | else | ||
226 | sd.sd_chip = C46; | ||
227 | sd.sd_MS = SEEMS; | ||
228 | sd.sd_RDY = SEERDY; | ||
229 | sd.sd_CS = SEECS; | ||
230 | sd.sd_CK = SEECK; | ||
231 | sd.sd_DO = SEEDO; | ||
232 | sd.sd_DI = SEEDI; | ||
233 | have_seeprom = ahc_acquire_seeprom(ahc, &sd); | ||
234 | } else | ||
235 | #endif | ||
236 | if ((ahc->chip & AHC_VL) != 0) { | ||
237 | sd.sd_control_offset = SEECTL_2840; | ||
238 | sd.sd_status_offset = STATUS_2840; | ||
239 | sd.sd_dataout_offset = STATUS_2840; | ||
240 | sd.sd_chip = C46; | ||
241 | sd.sd_MS = 0; | ||
242 | sd.sd_RDY = EEPROM_TF; | ||
243 | sd.sd_CS = CS_2840; | ||
244 | sd.sd_CK = CK_2840; | ||
245 | sd.sd_DO = DO_2840; | ||
246 | sd.sd_DI = DI_2840; | ||
247 | have_seeprom = TRUE; | ||
248 | } else { | ||
249 | printf("ahc_proc_write_seeprom: unsupported adapter type\n"); | ||
250 | goto done; | ||
251 | } | ||
252 | |||
253 | if (!have_seeprom) { | ||
254 | printf("ahc_proc_write_seeprom: No Serial EEPROM\n"); | ||
255 | goto done; | ||
256 | } else { | ||
257 | u_int start_addr; | ||
258 | |||
259 | if (ahc->seep_config == NULL) { | ||
260 | ahc->seep_config = malloc(sizeof(*ahc->seep_config), | ||
261 | M_DEVBUF, M_NOWAIT); | ||
262 | if (ahc->seep_config == NULL) { | ||
263 | printf("aic7xxx: Unable to allocate serial " | ||
264 | "eeprom buffer. Write failing\n"); | ||
265 | goto done; | ||
266 | } | ||
267 | } | ||
268 | printf("aic7xxx: Writing Serial EEPROM\n"); | ||
269 | start_addr = 32 * (ahc->channel - 'A'); | ||
270 | ahc_write_seeprom(&sd, (u_int16_t *)buffer, start_addr, | ||
271 | sizeof(struct seeprom_config)/2); | ||
272 | ahc_read_seeprom(&sd, (uint16_t *)ahc->seep_config, | ||
273 | start_addr, sizeof(struct seeprom_config)/2); | ||
274 | #if AHC_PCI_CONFIG > 0 | ||
275 | if ((ahc->chip & AHC_VL) == 0) | ||
276 | ahc_release_seeprom(&sd); | ||
277 | #endif | ||
278 | written = length; | ||
279 | } | ||
280 | |||
281 | done: | ||
282 | if (!paused) | ||
283 | ahc_unpause(ahc); | ||
284 | ahc_unlock(ahc, &s); | ||
285 | return (written); | ||
286 | } | ||
287 | |||
288 | /* | ||
289 | * Return information to handle /proc support for the driver. | ||
290 | */ | ||
291 | int | ||
292 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) | ||
293 | ahc_linux_proc_info(char *buffer, char **start, off_t offset, | ||
294 | int length, int hostno, int inout) | ||
295 | #else | ||
296 | ahc_linux_proc_info(struct Scsi_Host *shost, char *buffer, char **start, | ||
297 | off_t offset, int length, int inout) | ||
298 | #endif | ||
299 | { | ||
300 | struct ahc_softc *ahc; | ||
301 | struct info_str info; | ||
302 | char ahc_info[256]; | ||
303 | u_long s; | ||
304 | u_int max_targ; | ||
305 | u_int i; | ||
306 | int retval; | ||
307 | |||
308 | retval = -EINVAL; | ||
309 | ahc_list_lock(&s); | ||
310 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) | ||
311 | TAILQ_FOREACH(ahc, &ahc_tailq, links) { | ||
312 | if (ahc->platform_data->host->host_no == hostno) | ||
313 | break; | ||
314 | } | ||
315 | #else | ||
316 | ahc = ahc_find_softc(*(struct ahc_softc **)shost->hostdata); | ||
317 | #endif | ||
318 | |||
319 | if (ahc == NULL) | ||
320 | goto done; | ||
321 | |||
322 | /* Has data been written to the file? */ | ||
323 | if (inout == TRUE) { | ||
324 | retval = ahc_proc_write_seeprom(ahc, buffer, length); | ||
325 | goto done; | ||
326 | } | ||
327 | |||
328 | if (start) | ||
329 | *start = buffer; | ||
330 | |||
331 | info.buffer = buffer; | ||
332 | info.length = length; | ||
333 | info.offset = offset; | ||
334 | info.pos = 0; | ||
335 | |||
336 | copy_info(&info, "Adaptec AIC7xxx driver version: %s\n", | ||
337 | AIC7XXX_DRIVER_VERSION); | ||
338 | copy_info(&info, "%s\n", ahc->description); | ||
339 | ahc_controller_info(ahc, ahc_info); | ||
340 | copy_info(&info, "%s\n", ahc_info); | ||
341 | copy_info(&info, "Allocated SCBs: %d, SG List Length: %d\n\n", | ||
342 | ahc->scb_data->numscbs, AHC_NSEG); | ||
343 | |||
344 | |||
345 | if (ahc->seep_config == NULL) | ||
346 | copy_info(&info, "No Serial EEPROM\n"); | ||
347 | else { | ||
348 | copy_info(&info, "Serial EEPROM:\n"); | ||
349 | for (i = 0; i < sizeof(*ahc->seep_config)/2; i++) { | ||
350 | if (((i % 8) == 0) && (i != 0)) { | ||
351 | copy_info(&info, "\n"); | ||
352 | } | ||
353 | copy_info(&info, "0x%.4x ", | ||
354 | ((uint16_t*)ahc->seep_config)[i]); | ||
355 | } | ||
356 | copy_info(&info, "\n"); | ||
357 | } | ||
358 | copy_info(&info, "\n"); | ||
359 | |||
360 | max_targ = 15; | ||
361 | if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0) | ||
362 | max_targ = 7; | ||
363 | |||
364 | for (i = 0; i <= max_targ; i++) { | ||
365 | u_int our_id; | ||
366 | u_int target_id; | ||
367 | char channel; | ||
368 | |||
369 | channel = 'A'; | ||
370 | our_id = ahc->our_id; | ||
371 | target_id = i; | ||
372 | if (i > 7 && (ahc->features & AHC_TWIN) != 0) { | ||
373 | channel = 'B'; | ||
374 | our_id = ahc->our_id_b; | ||
375 | target_id = i % 8; | ||
376 | } | ||
377 | |||
378 | ahc_dump_target_state(ahc, &info, our_id, | ||
379 | channel, target_id, i); | ||
380 | } | ||
381 | retval = info.pos > info.offset ? info.pos - info.offset : 0; | ||
382 | done: | ||
383 | ahc_list_unlock(&s); | ||
384 | return (retval); | ||
385 | } | ||