aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/device_handler
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/device_handler')
-rw-r--r--drivers/scsi/device_handler/scsi_dh.c129
1 files changed, 105 insertions, 24 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
index a9159681ff27..a518f2eff19a 100644
--- a/drivers/scsi/device_handler/scsi_dh.c
+++ b/drivers/scsi/device_handler/scsi_dh.c
@@ -24,8 +24,16 @@
24#include <scsi/scsi_dh.h> 24#include <scsi/scsi_dh.h>
25#include "../scsi_priv.h" 25#include "../scsi_priv.h"
26 26
27struct scsi_dh_devinfo_list {
28 struct list_head node;
29 char vendor[9];
30 char model[17];
31 struct scsi_device_handler *handler;
32};
33
27static DEFINE_SPINLOCK(list_lock); 34static DEFINE_SPINLOCK(list_lock);
28static LIST_HEAD(scsi_dh_list); 35static LIST_HEAD(scsi_dh_list);
36static LIST_HEAD(scsi_dh_dev_list);
29 37
30static struct scsi_device_handler *get_device_handler(const char *name) 38static struct scsi_device_handler *get_device_handler(const char *name)
31{ 39{
@@ -42,21 +50,94 @@ static struct scsi_device_handler *get_device_handler(const char *name)
42 return found; 50 return found;
43} 51}
44 52
45static int device_handler_match(struct scsi_device_handler *tmp, 53
46 struct scsi_device *sdev) 54static struct scsi_device_handler *
55scsi_dh_cache_lookup(struct scsi_device *sdev)
47{ 56{
48 int i; 57 struct scsi_dh_devinfo_list *tmp;
49 58 struct scsi_device_handler *found_dh = NULL;
50 for(i = 0; tmp->devlist[i].vendor; i++) { 59
51 if (!strncmp(sdev->vendor, tmp->devlist[i].vendor, 60 spin_lock(&list_lock);
52 strlen(tmp->devlist[i].vendor)) && 61 list_for_each_entry(tmp, &scsi_dh_dev_list, node) {
53 !strncmp(sdev->model, tmp->devlist[i].model, 62 if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) &&
54 strlen(tmp->devlist[i].model))) { 63 !strncmp(sdev->model, tmp->model, strlen(tmp->model))) {
55 return 1; 64 found_dh = tmp->handler;
65 break;
56 } 66 }
57 } 67 }
68 spin_unlock(&list_lock);
58 69
59 return 0; 70 return found_dh;
71}
72
73static int scsi_dh_handler_lookup(struct scsi_device_handler *scsi_dh,
74 struct scsi_device *sdev)
75{
76 int i, found = 0;
77
78 for(i = 0; scsi_dh->devlist[i].vendor; i++) {
79 if (!strncmp(sdev->vendor, scsi_dh->devlist[i].vendor,
80 strlen(scsi_dh->devlist[i].vendor)) &&
81 !strncmp(sdev->model, scsi_dh->devlist[i].model,
82 strlen(scsi_dh->devlist[i].model))) {
83 found = 1;
84 break;
85 }
86 }
87 return found;
88}
89
90/*
91 * device_handler_match - Attach a device handler to a device
92 * @scsi_dh - The device handler to match against or NULL
93 * @sdev - SCSI device to be tested against @scsi_dh
94 *
95 * Tests @sdev against the device handler @scsi_dh or against
96 * all registered device_handler if @scsi_dh == NULL.
97 * Returns the found device handler or NULL if not found.
98 */
99static struct scsi_device_handler *
100device_handler_match(struct scsi_device_handler *scsi_dh,
101 struct scsi_device *sdev)
102{
103 struct scsi_device_handler *found_dh = NULL;
104 struct scsi_dh_devinfo_list *tmp;
105
106 found_dh = scsi_dh_cache_lookup(sdev);
107 if (found_dh)
108 return found_dh;
109
110 if (scsi_dh) {
111 if (scsi_dh_handler_lookup(scsi_dh, sdev))
112 found_dh = scsi_dh;
113 } else {
114 struct scsi_device_handler *tmp_dh;
115
116 spin_lock(&list_lock);
117 list_for_each_entry(tmp_dh, &scsi_dh_list, list) {
118 if (scsi_dh_handler_lookup(tmp_dh, sdev))
119 found_dh = tmp_dh;
120 }
121 spin_unlock(&list_lock);
122 }
123
124 if (found_dh) { /* If device is found, add it to the cache */
125 tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
126 if (tmp) {
127 strncpy(tmp->vendor, sdev->vendor, 8);
128 strncpy(tmp->model, sdev->model, 16);
129 tmp->vendor[8] = '\0';
130 tmp->model[16] = '\0';
131 tmp->handler = found_dh;
132 spin_lock(&list_lock);
133 list_add(&tmp->node, &scsi_dh_dev_list);
134 spin_unlock(&list_lock);
135 } else {
136 found_dh = NULL;
137 }
138 }
139
140 return found_dh;
60} 141}
61 142
62/* 143/*
@@ -203,26 +284,18 @@ static int scsi_dh_notifier(struct notifier_block *nb,
203 struct device *dev = data; 284 struct device *dev = data;
204 struct scsi_device *sdev; 285 struct scsi_device *sdev;
205 int err = 0; 286 int err = 0;
206 struct scsi_device_handler *tmp, *devinfo = NULL; 287 struct scsi_device_handler *devinfo = NULL;
207 288
208 if (!scsi_is_sdev_device(dev)) 289 if (!scsi_is_sdev_device(dev))
209 return 0; 290 return 0;
210 291
211 sdev = to_scsi_device(dev); 292 sdev = to_scsi_device(dev);
212 293
213 spin_lock(&list_lock);
214 list_for_each_entry(tmp, &scsi_dh_list, list) {
215 if (device_handler_match(tmp, sdev)) {
216 devinfo = tmp;
217 break;
218 }
219 }
220 spin_unlock(&list_lock);
221
222 if (!devinfo)
223 goto out;
224
225 if (action == BUS_NOTIFY_ADD_DEVICE) { 294 if (action == BUS_NOTIFY_ADD_DEVICE) {
295 devinfo = device_handler_match(NULL, sdev);
296 if (!devinfo)
297 goto out;
298
226 err = scsi_dh_handler_attach(sdev, devinfo); 299 err = scsi_dh_handler_attach(sdev, devinfo);
227 if (!err) 300 if (!err)
228 err = device_create_file(dev, &scsi_dh_state_attr); 301 err = device_create_file(dev, &scsi_dh_state_attr);
@@ -312,6 +385,8 @@ EXPORT_SYMBOL_GPL(scsi_register_device_handler);
312 */ 385 */
313int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) 386int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
314{ 387{
388 struct scsi_dh_devinfo_list *tmp, *pos;
389
315 if (!get_device_handler(scsi_dh->name)) 390 if (!get_device_handler(scsi_dh->name))
316 return -ENODEV; 391 return -ENODEV;
317 392
@@ -320,6 +395,12 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
320 395
321 spin_lock(&list_lock); 396 spin_lock(&list_lock);
322 list_del(&scsi_dh->list); 397 list_del(&scsi_dh->list);
398 list_for_each_entry_safe(pos, tmp, &scsi_dh_dev_list, node) {
399 if (pos->handler == scsi_dh) {
400 list_del(&pos->node);
401 kfree(pos);
402 }
403 }
323 spin_unlock(&list_lock); 404 spin_unlock(&list_lock);
324 printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); 405 printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name);
325 406