diff options
Diffstat (limited to 'drivers/scsi/scsi_devinfo.c')
-rw-r--r-- | drivers/scsi/scsi_devinfo.c | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c new file mode 100644 index 000000000000..6121dc1bfada --- /dev/null +++ b/drivers/scsi/scsi_devinfo.c | |||
@@ -0,0 +1,564 @@ | |||
1 | |||
2 | #include <linux/blkdev.h> | ||
3 | #include <linux/init.h> | ||
4 | #include <linux/kernel.h> | ||
5 | #include <linux/module.h> | ||
6 | #include <linux/moduleparam.h> | ||
7 | #include <linux/proc_fs.h> | ||
8 | #include <linux/seq_file.h> | ||
9 | |||
10 | #include <scsi/scsi_device.h> | ||
11 | #include <scsi/scsi_devinfo.h> | ||
12 | |||
13 | #include "scsi_priv.h" | ||
14 | |||
15 | |||
16 | /* | ||
17 | * scsi_dev_info_list: structure to hold black/white listed devices. | ||
18 | */ | ||
19 | struct scsi_dev_info_list { | ||
20 | struct list_head dev_info_list; | ||
21 | char vendor[8]; | ||
22 | char model[16]; | ||
23 | unsigned flags; | ||
24 | unsigned compatible; /* for use with scsi_static_device_list entries */ | ||
25 | }; | ||
26 | |||
27 | |||
28 | static const char spaces[] = " "; /* 16 of them */ | ||
29 | static unsigned scsi_default_dev_flags; | ||
30 | static LIST_HEAD(scsi_dev_info_list); | ||
31 | static char scsi_dev_flags[256]; | ||
32 | |||
33 | /* | ||
34 | * scsi_static_device_list: deprecated list of devices that require | ||
35 | * settings that differ from the default, includes black-listed (broken) | ||
36 | * devices. The entries here are added to the tail of scsi_dev_info_list | ||
37 | * via scsi_dev_info_list_init. | ||
38 | * | ||
39 | * Do not add to this list, use the command line or proc interface to add | ||
40 | * to the scsi_dev_info_list. This table will eventually go away. | ||
41 | */ | ||
42 | static struct { | ||
43 | char *vendor; | ||
44 | char *model; | ||
45 | char *revision; /* revision known to be bad, unused */ | ||
46 | unsigned flags; | ||
47 | } scsi_static_device_list[] __initdata = { | ||
48 | /* | ||
49 | * The following devices are known not to tolerate a lun != 0 scan | ||
50 | * for one reason or another. Some will respond to all luns, | ||
51 | * others will lock up. | ||
52 | */ | ||
53 | {"Aashima", "IMAGERY 2400SP", "1.03", BLIST_NOLUN}, /* locks up */ | ||
54 | {"CHINON", "CD-ROM CDS-431", "H42", BLIST_NOLUN}, /* locks up */ | ||
55 | {"CHINON", "CD-ROM CDS-535", "Q14", BLIST_NOLUN}, /* locks up */ | ||
56 | {"DENON", "DRD-25X", "V", BLIST_NOLUN}, /* locks up */ | ||
57 | {"HITACHI", "DK312C", "CM81", BLIST_NOLUN}, /* responds to all lun */ | ||
58 | {"HITACHI", "DK314C", "CR21", BLIST_NOLUN}, /* responds to all lun */ | ||
59 | {"IMS", "CDD521/10", "2.06", BLIST_NOLUN}, /* locks up */ | ||
60 | {"MAXTOR", "XT-3280", "PR02", BLIST_NOLUN}, /* locks up */ | ||
61 | {"MAXTOR", "XT-4380S", "B3C", BLIST_NOLUN}, /* locks up */ | ||
62 | {"MAXTOR", "MXT-1240S", "I1.2", BLIST_NOLUN}, /* locks up */ | ||
63 | {"MAXTOR", "XT-4170S", "B5A", BLIST_NOLUN}, /* locks up */ | ||
64 | {"MAXTOR", "XT-8760S", "B7B", BLIST_NOLUN}, /* locks up */ | ||
65 | {"MEDIAVIS", "RENO CD-ROMX2A", "2.03", BLIST_NOLUN}, /* responds to all lun */ | ||
66 | {"MICROTEK", "ScanMakerIII", "2.30", BLIST_NOLUN}, /* responds to all lun */ | ||
67 | {"NEC", "CD-ROM DRIVE:841", "1.0", BLIST_NOLUN},/* locks up */ | ||
68 | {"PHILIPS", "PCA80SC", "V4-2", BLIST_NOLUN}, /* responds to all lun */ | ||
69 | {"RODIME", "RO3000S", "2.33", BLIST_NOLUN}, /* locks up */ | ||
70 | {"SUN", "SENA", NULL, BLIST_NOLUN}, /* responds to all luns */ | ||
71 | /* | ||
72 | * The following causes a failed REQUEST SENSE on lun 1 for | ||
73 | * aha152x controller, which causes SCSI code to reset bus. | ||
74 | */ | ||
75 | {"SANYO", "CRD-250S", "1.20", BLIST_NOLUN}, | ||
76 | /* | ||
77 | * The following causes a failed REQUEST SENSE on lun 1 for | ||
78 | * aha152x controller, which causes SCSI code to reset bus. | ||
79 | */ | ||
80 | {"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN}, | ||
81 | {"SEAGATE", "ST296", "921", BLIST_NOLUN}, /* responds to all lun */ | ||
82 | {"SEAGATE", "ST1581", "6538", BLIST_NOLUN}, /* responds to all lun */ | ||
83 | {"SONY", "CD-ROM CDU-541", "4.3d", BLIST_NOLUN}, | ||
84 | {"SONY", "CD-ROM CDU-55S", "1.0i", BLIST_NOLUN}, | ||
85 | {"SONY", "CD-ROM CDU-561", "1.7x", BLIST_NOLUN}, | ||
86 | {"SONY", "CD-ROM CDU-8012", NULL, BLIST_NOLUN}, | ||
87 | {"SONY", "SDT-5000", "3.17", BLIST_SELECT_NO_ATN}, | ||
88 | {"TANDBERG", "TDC 3600", "U07", BLIST_NOLUN}, /* locks up */ | ||
89 | {"TEAC", "CD-R55S", "1.0H", BLIST_NOLUN}, /* locks up */ | ||
90 | /* | ||
91 | * The following causes a failed REQUEST SENSE on lun 1 for | ||
92 | * seagate controller, which causes SCSI code to reset bus. | ||
93 | */ | ||
94 | {"TEAC", "CD-ROM", "1.06", BLIST_NOLUN}, | ||
95 | {"TEAC", "MT-2ST/45S2-27", "RV M", BLIST_NOLUN}, /* responds to all lun */ | ||
96 | /* | ||
97 | * The following causes a failed REQUEST SENSE on lun 1 for | ||
98 | * seagate controller, which causes SCSI code to reset bus. | ||
99 | */ | ||
100 | {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */ | ||
101 | {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */ | ||
102 | {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */ | ||
103 | {"MEDIAVIS", "CDR-H93MV", "1.31", BLIST_NOLUN}, /* locks up */ | ||
104 | {"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all lun */ | ||
105 | {"MITSUMI", "CD-R CR-2201CS", "6119", BLIST_NOLUN}, /* locks up */ | ||
106 | {"NEC", "D3856", "0009", BLIST_NOLUN}, | ||
107 | {"QUANTUM", "LPS525S", "3110", BLIST_NOLUN}, /* locks up */ | ||
108 | {"QUANTUM", "PD1225S", "3110", BLIST_NOLUN}, /* locks up */ | ||
109 | {"QUANTUM", "FIREBALL ST4.3S", "0F0C", BLIST_NOLUN}, /* locks up */ | ||
110 | {"RELISYS", "Scorpio", NULL, BLIST_NOLUN}, /* responds to all lun */ | ||
111 | {"SANKYO", "CP525", "6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */ | ||
112 | {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN}, | ||
113 | {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* locks up */ | ||
114 | {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* locks up */ | ||
115 | {"YAMAHA", "CRW8424S", "1.0", BLIST_NOLUN}, /* locks up */ | ||
116 | {"YAMAHA", "CRW6416S", "1.0c", BLIST_NOLUN}, /* locks up */ | ||
117 | |||
118 | /* | ||
119 | * Other types of devices that have special flags. | ||
120 | * Note that all USB devices should have the BLIST_INQUIRY_36 flag. | ||
121 | */ | ||
122 | {"3PARdata", "VV", NULL, BLIST_REPORTLUN2}, | ||
123 | {"ADAPTEC", "AACRAID", NULL, BLIST_FORCELUN}, | ||
124 | {"ADAPTEC", "Adaptec 5400S", NULL, BLIST_FORCELUN}, | ||
125 | {"AFT PRO", "-IX CF", "0.0>", BLIST_FORCELUN}, | ||
126 | {"BELKIN", "USB 2 HS-CF", "1.95", BLIST_FORCELUN | BLIST_INQUIRY_36}, | ||
127 | {"CANON", "IPUBJD", NULL, BLIST_SPARSELUN}, | ||
128 | {"CBOX3", "USB Storage-SMC", "300A", BLIST_FORCELUN | BLIST_INQUIRY_36}, | ||
129 | {"CMD", "CRA-7280", NULL, BLIST_SPARSELUN}, /* CMD RAID Controller */ | ||
130 | {"CNSI", "G7324", NULL, BLIST_SPARSELUN}, /* Chaparral G7324 RAID */ | ||
131 | {"CNSi", "G8324", NULL, BLIST_SPARSELUN}, /* Chaparral G8324 RAID */ | ||
132 | {"COMPAQ", "LOGICAL VOLUME", NULL, BLIST_FORCELUN}, | ||
133 | {"COMPAQ", "CR3500", NULL, BLIST_FORCELUN}, | ||
134 | {"COMPAQ", "MSA1000", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, | ||
135 | {"COMPAQ", "MSA1000 VOLUME", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, | ||
136 | {"COMPAQ", "HSV110", NULL, BLIST_REPORTLUN2 | BLIST_NOSTARTONADD}, | ||
137 | {"DDN", "SAN DataDirector", "*", BLIST_SPARSELUN}, | ||
138 | {"DEC", "HSG80", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, | ||
139 | {"DELL", "PV660F", NULL, BLIST_SPARSELUN}, | ||
140 | {"DELL", "PV660F PSEUDO", NULL, BLIST_SPARSELUN}, | ||
141 | {"DELL", "PSEUDO DEVICE .", NULL, BLIST_SPARSELUN}, /* Dell PV 530F */ | ||
142 | {"DELL", "PV530F", NULL, BLIST_SPARSELUN}, | ||
143 | {"DELL", "PERCRAID", NULL, BLIST_FORCELUN}, | ||
144 | {"DGC", "RAID", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, storage on LUN 0 */ | ||
145 | {"DGC", "DISK", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, no storage on LUN 0 */ | ||
146 | {"EMC", "SYMMETRIX", NULL, BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_FORCELUN}, | ||
147 | {"EMULEX", "MD21/S2 ESDI", NULL, BLIST_SINGLELUN}, | ||
148 | {"FSC", "CentricStor", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, | ||
149 | {"Generic", "USB SD Reader", "1.00", BLIST_FORCELUN | BLIST_INQUIRY_36}, | ||
150 | {"Generic", "USB Storage-SMC", "0180", BLIST_FORCELUN | BLIST_INQUIRY_36}, | ||
151 | {"Generic", "USB Storage-SMC", "0207", BLIST_FORCELUN | BLIST_INQUIRY_36}, | ||
152 | {"HITACHI", "DF400", "*", BLIST_SPARSELUN}, | ||
153 | {"HITACHI", "DF500", "*", BLIST_SPARSELUN}, | ||
154 | {"HITACHI", "DF600", "*", BLIST_SPARSELUN}, | ||
155 | {"HP", "A6189A", NULL, BLIST_SPARSELUN | BLIST_LARGELUN}, /* HP VA7400 */ | ||
156 | {"HP", "OPEN-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, /* HP XP Arrays */ | ||
157 | {"HP", "NetRAID-4M", NULL, BLIST_FORCELUN}, | ||
158 | {"HP", "HSV100", NULL, BLIST_REPORTLUN2 | BLIST_NOSTARTONADD}, | ||
159 | {"HP", "C1557A", NULL, BLIST_FORCELUN}, | ||
160 | {"HP", "C3323-300", "4269", BLIST_NOTQ}, | ||
161 | {"IBM", "AuSaV1S2", NULL, BLIST_FORCELUN}, | ||
162 | {"IBM", "ProFibre 4000R", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, | ||
163 | {"IBM", "2105", NULL, BLIST_RETRY_HWERROR}, | ||
164 | {"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN}, | ||
165 | {"IOMEGA", "Io20S *F", NULL, BLIST_KEY}, | ||
166 | {"INSITE", "Floptical F*8I", NULL, BLIST_KEY}, | ||
167 | {"INSITE", "I325VM", NULL, BLIST_KEY}, | ||
168 | {"iRiver", "iFP Mass Driver", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36}, | ||
169 | {"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN}, | ||
170 | {"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, | ||
171 | {"MATSHITA", "DMC-LC5", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36}, | ||
172 | {"MATSHITA", "DMC-LC40", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36}, | ||
173 | {"Medion", "Flash XL MMC/SD", "2.6D", BLIST_FORCELUN}, | ||
174 | {"MegaRAID", "LD", NULL, BLIST_FORCELUN}, | ||
175 | {"MICROP", "4110", NULL, BLIST_NOTQ}, | ||
176 | {"MYLEX", "DACARMRB", "*", BLIST_REPORTLUN2}, | ||
177 | {"nCipher", "Fastness Crypto", NULL, BLIST_FORCELUN}, | ||
178 | {"NAKAMICH", "MJ-4.8S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, | ||
179 | {"NAKAMICH", "MJ-5.16S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, | ||
180 | {"NEC", "PD-1 ODX654P", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, | ||
181 | {"NRC", "MBR-7", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, | ||
182 | {"NRC", "MBR-7.4", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, | ||
183 | {"PIONEER", "CD-ROM DRM-600", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, | ||
184 | {"PIONEER", "CD-ROM DRM-602X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, | ||
185 | {"PIONEER", "CD-ROM DRM-604X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, | ||
186 | {"REGAL", "CDC-4X", NULL, BLIST_MAX5LUN | BLIST_SINGLELUN}, | ||
187 | {"SanDisk", "ImageMate CF-SD1", NULL, BLIST_FORCELUN}, | ||
188 | {"SEAGATE", "ST34555N", "0930", BLIST_NOTQ}, /* Chokes on tagged INQUIRY */ | ||
189 | {"SEAGATE", "ST3390N", "9546", BLIST_NOTQ}, | ||
190 | {"SGI", "RAID3", "*", BLIST_SPARSELUN}, | ||
191 | {"SGI", "RAID5", "*", BLIST_SPARSELUN}, | ||
192 | {"SGI", "TP9100", "*", BLIST_REPORTLUN2}, | ||
193 | {"SGI", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, | ||
194 | {"SMSC", "USB 2 HS-CF", NULL, BLIST_SPARSELUN | BLIST_INQUIRY_36}, | ||
195 | {"SONY", "CD-ROM CDU-8001", NULL, BLIST_BORKEN}, | ||
196 | {"SONY", "TSL", NULL, BLIST_FORCELUN}, /* DDS3 & DDS4 autoloaders */ | ||
197 | {"ST650211", "CF", NULL, BLIST_RETRY_HWERROR}, | ||
198 | {"SUN", "T300", "*", BLIST_SPARSELUN}, | ||
199 | {"SUN", "T4", "*", BLIST_SPARSELUN}, | ||
200 | {"TEXEL", "CD-ROM", "1.06", BLIST_BORKEN}, | ||
201 | {"TOSHIBA", "CDROM", NULL, BLIST_ISROM}, | ||
202 | {"TOSHIBA", "CD-ROM", NULL, BLIST_ISROM}, | ||
203 | {"USB2.0", "SMARTMEDIA/XD", NULL, BLIST_FORCELUN | BLIST_INQUIRY_36}, | ||
204 | {"WangDAT", "Model 2600", "01.7", BLIST_SELECT_NO_ATN}, | ||
205 | {"WangDAT", "Model 3200", "02.2", BLIST_SELECT_NO_ATN}, | ||
206 | {"WangDAT", "Model 1300", "02.4", BLIST_SELECT_NO_ATN}, | ||
207 | {"WDC WD25", "00JB-00FUA0", NULL, BLIST_NOREPORTLUN}, | ||
208 | {"XYRATEX", "RS", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, | ||
209 | {"Zzyzx", "RocketStor 500S", NULL, BLIST_SPARSELUN}, | ||
210 | {"Zzyzx", "RocketStor 2000", NULL, BLIST_SPARSELUN}, | ||
211 | { NULL, NULL, NULL, 0 }, | ||
212 | }; | ||
213 | |||
214 | /* | ||
215 | * scsi_strcpy_devinfo: called from scsi_dev_info_list_add to copy into | ||
216 | * devinfo vendor and model strings. | ||
217 | */ | ||
218 | static void scsi_strcpy_devinfo(char *name, char *to, size_t to_length, | ||
219 | char *from, int compatible) | ||
220 | { | ||
221 | size_t from_length; | ||
222 | |||
223 | from_length = strlen(from); | ||
224 | strncpy(to, from, min(to_length, from_length)); | ||
225 | if (from_length < to_length) { | ||
226 | if (compatible) { | ||
227 | /* | ||
228 | * NUL terminate the string if it is short. | ||
229 | */ | ||
230 | to[from_length] = '\0'; | ||
231 | } else { | ||
232 | /* | ||
233 | * space pad the string if it is short. | ||
234 | */ | ||
235 | strncpy(&to[from_length], spaces, | ||
236 | to_length - from_length); | ||
237 | } | ||
238 | } | ||
239 | if (from_length > to_length) | ||
240 | printk(KERN_WARNING "%s: %s string '%s' is too long\n", | ||
241 | __FUNCTION__, name, from); | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * scsi_dev_info_list_add: add one dev_info list entry. | ||
246 | * @vendor: vendor string | ||
247 | * @model: model (product) string | ||
248 | * @strflags: integer string | ||
249 | * @flag: if strflags NULL, use this flag value | ||
250 | * | ||
251 | * Description: | ||
252 | * Create and add one dev_info entry for @vendor, @model, @strflags or | ||
253 | * @flag. If @compatible, add to the tail of the list, do not space | ||
254 | * pad, and set devinfo->compatible. The scsi_static_device_list entries | ||
255 | * are added with @compatible 1 and @clfags NULL. | ||
256 | * | ||
257 | * Returns: 0 OK, -error on failure. | ||
258 | **/ | ||
259 | static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, | ||
260 | char *strflags, int flags) | ||
261 | { | ||
262 | struct scsi_dev_info_list *devinfo; | ||
263 | |||
264 | devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL); | ||
265 | if (!devinfo) { | ||
266 | printk(KERN_ERR "%s: no memory\n", __FUNCTION__); | ||
267 | return -ENOMEM; | ||
268 | } | ||
269 | |||
270 | scsi_strcpy_devinfo("vendor", devinfo->vendor, sizeof(devinfo->vendor), | ||
271 | vendor, compatible); | ||
272 | scsi_strcpy_devinfo("model", devinfo->model, sizeof(devinfo->model), | ||
273 | model, compatible); | ||
274 | |||
275 | if (strflags) | ||
276 | devinfo->flags = simple_strtoul(strflags, NULL, 0); | ||
277 | else | ||
278 | devinfo->flags = flags; | ||
279 | |||
280 | devinfo->compatible = compatible; | ||
281 | |||
282 | if (compatible) | ||
283 | list_add_tail(&devinfo->dev_info_list, &scsi_dev_info_list); | ||
284 | else | ||
285 | list_add(&devinfo->dev_info_list, &scsi_dev_info_list); | ||
286 | |||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | /** | ||
291 | * scsi_dev_info_list_add_str: parse dev_list and add to the | ||
292 | * scsi_dev_info_list. | ||
293 | * @dev_list: string of device flags to add | ||
294 | * | ||
295 | * Description: | ||
296 | * Parse dev_list, and add entries to the scsi_dev_info_list. | ||
297 | * dev_list is of the form "vendor:product:flag,vendor:product:flag". | ||
298 | * dev_list is modified via strsep. Can be called for command line | ||
299 | * addition, for proc or mabye a sysfs interface. | ||
300 | * | ||
301 | * Returns: 0 if OK, -error on failure. | ||
302 | **/ | ||
303 | static int scsi_dev_info_list_add_str(char *dev_list) | ||
304 | { | ||
305 | char *vendor, *model, *strflags, *next; | ||
306 | char *next_check; | ||
307 | int res = 0; | ||
308 | |||
309 | next = dev_list; | ||
310 | if (next && next[0] == '"') { | ||
311 | /* | ||
312 | * Ignore both the leading and trailing quote. | ||
313 | */ | ||
314 | next++; | ||
315 | next_check = ",\""; | ||
316 | } else { | ||
317 | next_check = ","; | ||
318 | } | ||
319 | |||
320 | /* | ||
321 | * For the leading and trailing '"' case, the for loop comes | ||
322 | * through the last time with vendor[0] == '\0'. | ||
323 | */ | ||
324 | for (vendor = strsep(&next, ":"); vendor && (vendor[0] != '\0') | ||
325 | && (res == 0); vendor = strsep(&next, ":")) { | ||
326 | strflags = NULL; | ||
327 | model = strsep(&next, ":"); | ||
328 | if (model) | ||
329 | strflags = strsep(&next, next_check); | ||
330 | if (!model || !strflags) { | ||
331 | printk(KERN_ERR "%s: bad dev info string '%s' '%s'" | ||
332 | " '%s'\n", __FUNCTION__, vendor, model, | ||
333 | strflags); | ||
334 | res = -EINVAL; | ||
335 | } else | ||
336 | res = scsi_dev_info_list_add(0 /* compatible */, vendor, | ||
337 | model, strflags, 0); | ||
338 | } | ||
339 | return res; | ||
340 | } | ||
341 | |||
342 | /** | ||
343 | * get_device_flags - get device specific flags from the dynamic device | ||
344 | * list. Called during scan time. | ||
345 | * @vendor: vendor name | ||
346 | * @model: model name | ||
347 | * | ||
348 | * Description: | ||
349 | * Search the scsi_dev_info_list for an entry matching @vendor and | ||
350 | * @model, if found, return the matching flags value, else return | ||
351 | * the host or global default settings. | ||
352 | **/ | ||
353 | int scsi_get_device_flags(struct scsi_device *sdev, unsigned char *vendor, | ||
354 | unsigned char *model) | ||
355 | { | ||
356 | struct scsi_dev_info_list *devinfo; | ||
357 | unsigned int bflags; | ||
358 | |||
359 | bflags = sdev->sdev_bflags; | ||
360 | if (!bflags) | ||
361 | bflags = scsi_default_dev_flags; | ||
362 | |||
363 | list_for_each_entry(devinfo, &scsi_dev_info_list, dev_info_list) { | ||
364 | if (devinfo->compatible) { | ||
365 | /* | ||
366 | * Behave like the older version of get_device_flags. | ||
367 | */ | ||
368 | size_t max; | ||
369 | /* | ||
370 | * XXX why skip leading spaces? If an odd INQUIRY | ||
371 | * value, that should have been part of the | ||
372 | * scsi_static_device_list[] entry, such as " FOO" | ||
373 | * rather than "FOO". Since this code is already | ||
374 | * here, and we don't know what device it is | ||
375 | * trying to work with, leave it as-is. | ||
376 | */ | ||
377 | max = 8; /* max length of vendor */ | ||
378 | while ((max > 0) && *vendor == ' ') { | ||
379 | max--; | ||
380 | vendor++; | ||
381 | } | ||
382 | /* | ||
383 | * XXX removing the following strlen() would be | ||
384 | * good, using it means that for a an entry not in | ||
385 | * the list, we scan every byte of every vendor | ||
386 | * listed in scsi_static_device_list[], and never match | ||
387 | * a single one (and still have to compare at | ||
388 | * least the first byte of each vendor). | ||
389 | */ | ||
390 | if (memcmp(devinfo->vendor, vendor, | ||
391 | min(max, strlen(devinfo->vendor)))) | ||
392 | continue; | ||
393 | /* | ||
394 | * Skip spaces again. | ||
395 | */ | ||
396 | max = 16; /* max length of model */ | ||
397 | while ((max > 0) && *model == ' ') { | ||
398 | max--; | ||
399 | model++; | ||
400 | } | ||
401 | if (memcmp(devinfo->model, model, | ||
402 | min(max, strlen(devinfo->model)))) | ||
403 | continue; | ||
404 | return devinfo->flags; | ||
405 | } else { | ||
406 | if (!memcmp(devinfo->vendor, vendor, | ||
407 | sizeof(devinfo->vendor)) && | ||
408 | !memcmp(devinfo->model, model, | ||
409 | sizeof(devinfo->model))) | ||
410 | return devinfo->flags; | ||
411 | } | ||
412 | } | ||
413 | return bflags; | ||
414 | } | ||
415 | |||
416 | #ifdef CONFIG_SCSI_PROC_FS | ||
417 | /* | ||
418 | * proc_scsi_dev_info_read: dump the scsi_dev_info_list via | ||
419 | * /proc/scsi/device_info | ||
420 | */ | ||
421 | static int proc_scsi_devinfo_read(char *buffer, char **start, | ||
422 | off_t offset, int length) | ||
423 | { | ||
424 | struct scsi_dev_info_list *devinfo; | ||
425 | int size, len = 0; | ||
426 | off_t begin = 0; | ||
427 | off_t pos = 0; | ||
428 | |||
429 | list_for_each_entry(devinfo, &scsi_dev_info_list, dev_info_list) { | ||
430 | size = sprintf(buffer + len, "'%.8s' '%.16s' 0x%x\n", | ||
431 | devinfo->vendor, devinfo->model, devinfo->flags); | ||
432 | len += size; | ||
433 | pos = begin + len; | ||
434 | if (pos < offset) { | ||
435 | len = 0; | ||
436 | begin = pos; | ||
437 | } | ||
438 | if (pos > offset + length) | ||
439 | goto stop_output; | ||
440 | } | ||
441 | |||
442 | stop_output: | ||
443 | *start = buffer + (offset - begin); /* Start of wanted data */ | ||
444 | len -= (offset - begin); /* Start slop */ | ||
445 | if (len > length) | ||
446 | len = length; /* Ending slop */ | ||
447 | return (len); | ||
448 | } | ||
449 | |||
450 | /* | ||
451 | * proc_scsi_dev_info_write: allow additions to the scsi_dev_info_list via | ||
452 | * /proc. | ||
453 | * | ||
454 | * Use: echo "vendor:model:flag" > /proc/scsi/device_info | ||
455 | * | ||
456 | * To add a black/white list entry for vendor and model with an integer | ||
457 | * value of flag to the scsi device info list. | ||
458 | */ | ||
459 | static int proc_scsi_devinfo_write(struct file *file, const char __user *buf, | ||
460 | unsigned long length, void *data) | ||
461 | { | ||
462 | char *buffer; | ||
463 | int err = length; | ||
464 | |||
465 | if (!buf || length>PAGE_SIZE) | ||
466 | return -EINVAL; | ||
467 | if (!(buffer = (char *) __get_free_page(GFP_KERNEL))) | ||
468 | return -ENOMEM; | ||
469 | if (copy_from_user(buffer, buf, length)) { | ||
470 | err =-EFAULT; | ||
471 | goto out; | ||
472 | } | ||
473 | |||
474 | if (length < PAGE_SIZE) | ||
475 | buffer[length] = '\0'; | ||
476 | else if (buffer[PAGE_SIZE-1]) { | ||
477 | err = -EINVAL; | ||
478 | goto out; | ||
479 | } | ||
480 | |||
481 | scsi_dev_info_list_add_str(buffer); | ||
482 | |||
483 | out: | ||
484 | free_page((unsigned long)buffer); | ||
485 | return err; | ||
486 | } | ||
487 | #endif /* CONFIG_SCSI_PROC_FS */ | ||
488 | |||
489 | module_param_string(dev_flags, scsi_dev_flags, sizeof(scsi_dev_flags), 0); | ||
490 | MODULE_PARM_DESC(dev_flags, | ||
491 | "Given scsi_dev_flags=vendor:model:flags[,v:m:f] add black/white" | ||
492 | " list entries for vendor and model with an integer value of flags" | ||
493 | " to the scsi device info list"); | ||
494 | |||
495 | module_param_named(default_dev_flags, scsi_default_dev_flags, int, S_IRUGO|S_IWUSR); | ||
496 | MODULE_PARM_DESC(default_dev_flags, | ||
497 | "scsi default device flag integer value"); | ||
498 | |||
499 | /** | ||
500 | * scsi_dev_info_list_delete: called from scsi.c:exit_scsi to remove | ||
501 | * the scsi_dev_info_list. | ||
502 | **/ | ||
503 | void scsi_exit_devinfo(void) | ||
504 | { | ||
505 | struct list_head *lh, *lh_next; | ||
506 | struct scsi_dev_info_list *devinfo; | ||
507 | |||
508 | #ifdef CONFIG_SCSI_PROC_FS | ||
509 | remove_proc_entry("scsi/device_info", NULL); | ||
510 | #endif | ||
511 | |||
512 | list_for_each_safe(lh, lh_next, &scsi_dev_info_list) { | ||
513 | devinfo = list_entry(lh, struct scsi_dev_info_list, | ||
514 | dev_info_list); | ||
515 | kfree(devinfo); | ||
516 | } | ||
517 | } | ||
518 | |||
519 | /** | ||
520 | * scsi_dev_list_init: set up the dynamic device list. | ||
521 | * @dev_list: string of device flags to add | ||
522 | * | ||
523 | * Description: | ||
524 | * Add command line @dev_list entries, then add | ||
525 | * scsi_static_device_list entries to the scsi device info list. | ||
526 | **/ | ||
527 | int __init scsi_init_devinfo(void) | ||
528 | { | ||
529 | #ifdef CONFIG_SCSI_PROC_FS | ||
530 | struct proc_dir_entry *p; | ||
531 | #endif | ||
532 | int error, i; | ||
533 | |||
534 | error = scsi_dev_info_list_add_str(scsi_dev_flags); | ||
535 | if (error) | ||
536 | return error; | ||
537 | |||
538 | for (i = 0; scsi_static_device_list[i].vendor; i++) { | ||
539 | error = scsi_dev_info_list_add(1 /* compatibile */, | ||
540 | scsi_static_device_list[i].vendor, | ||
541 | scsi_static_device_list[i].model, | ||
542 | NULL, | ||
543 | scsi_static_device_list[i].flags); | ||
544 | if (error) | ||
545 | goto out; | ||
546 | } | ||
547 | |||
548 | #ifdef CONFIG_SCSI_PROC_FS | ||
549 | p = create_proc_entry("scsi/device_info", 0, NULL); | ||
550 | if (!p) { | ||
551 | error = -ENOMEM; | ||
552 | goto out; | ||
553 | } | ||
554 | |||
555 | p->owner = THIS_MODULE; | ||
556 | p->get_info = proc_scsi_devinfo_read; | ||
557 | p->write_proc = proc_scsi_devinfo_write; | ||
558 | #endif /* CONFIG_SCSI_PROC_FS */ | ||
559 | |||
560 | out: | ||
561 | if (error) | ||
562 | scsi_exit_devinfo(); | ||
563 | return error; | ||
564 | } | ||