diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-01-08 09:31:11 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-02-14 09:55:02 -0500 |
commit | c95571e68086d36e8e3369597b03ec29c63abec9 (patch) | |
tree | 20cb4b0f1f9bbf49375ab6a6458668fd726f6841 | |
parent | 57985d7e1e48f16548aa6904264e21bca15af0fc (diff) |
s390/3270: introduce device notifier
Add a notifier to create / destroy the device nodes for the tty view
and the fullscreen view. Only device nodes for online devices are
created and the device names will follow the convention as outlined
in Documentation/devices.txt: 3270/tty<x> for the tty nodes,
3270/tub<x> for hte fullscreen nodes and 3270/tub for the fullscreen
control node.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | drivers/s390/char/fs3270.c | 29 | ||||
-rw-r--r-- | drivers/s390/char/raw3270.c | 76 | ||||
-rw-r--r-- | drivers/s390/char/raw3270.h | 11 | ||||
-rw-r--r-- | drivers/s390/char/tty3270.c | 49 |
4 files changed, 83 insertions, 82 deletions
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 911704571b9c..230697aac94b 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c | |||
@@ -443,7 +443,7 @@ fs3270_open(struct inode *inode, struct file *filp) | |||
443 | tty_kref_put(tty); | 443 | tty_kref_put(tty); |
444 | return -ENODEV; | 444 | return -ENODEV; |
445 | } | 445 | } |
446 | minor = tty->index + RAW3270_FIRSTMINOR; | 446 | minor = tty->index; |
447 | tty_kref_put(tty); | 447 | tty_kref_put(tty); |
448 | } | 448 | } |
449 | mutex_lock(&fs3270_mutex); | 449 | mutex_lock(&fs3270_mutex); |
@@ -524,6 +524,25 @@ static const struct file_operations fs3270_fops = { | |||
524 | .llseek = no_llseek, | 524 | .llseek = no_llseek, |
525 | }; | 525 | }; |
526 | 526 | ||
527 | void fs3270_create_cb(int minor) | ||
528 | { | ||
529 | __register_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub", &fs3270_fops); | ||
530 | device_create(class3270, NULL, MKDEV(IBM_FS3270_MAJOR, minor), | ||
531 | NULL, "3270/tub%d", minor); | ||
532 | } | ||
533 | |||
534 | void fs3270_destroy_cb(int minor) | ||
535 | { | ||
536 | device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, minor)); | ||
537 | __unregister_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub"); | ||
538 | } | ||
539 | |||
540 | struct raw3270_notifier fs3270_notifier = | ||
541 | { | ||
542 | .create = fs3270_create_cb, | ||
543 | .destroy = fs3270_destroy_cb, | ||
544 | }; | ||
545 | |||
527 | /* | 546 | /* |
528 | * 3270 fullscreen driver initialization. | 547 | * 3270 fullscreen driver initialization. |
529 | */ | 548 | */ |
@@ -532,16 +551,20 @@ fs3270_init(void) | |||
532 | { | 551 | { |
533 | int rc; | 552 | int rc; |
534 | 553 | ||
535 | rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops); | 554 | rc = __register_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270", &fs3270_fops); |
536 | if (rc) | 555 | if (rc) |
537 | return rc; | 556 | return rc; |
557 | device_create(class3270, NULL, MKDEV(IBM_FS3270_MAJOR, 0), | ||
558 | NULL, "3270/tub"); | ||
559 | raw3270_register_notifier(&fs3270_notifier); | ||
538 | return 0; | 560 | return 0; |
539 | } | 561 | } |
540 | 562 | ||
541 | static void __exit | 563 | static void __exit |
542 | fs3270_exit(void) | 564 | fs3270_exit(void) |
543 | { | 565 | { |
544 | unregister_chrdev(IBM_FS3270_MAJOR, "fs3270"); | 566 | raw3270_unregister_notifier(&fs3270_notifier); |
567 | __unregister_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270"); | ||
545 | } | 568 | } |
546 | 569 | ||
547 | MODULE_LICENSE("GPL"); | 570 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 9a6c140c5f07..72f69fda9d01 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c | |||
@@ -28,7 +28,7 @@ | |||
28 | #include <linux/device.h> | 28 | #include <linux/device.h> |
29 | #include <linux/mutex.h> | 29 | #include <linux/mutex.h> |
30 | 30 | ||
31 | static struct class *class3270; | 31 | struct class *class3270; |
32 | 32 | ||
33 | /* The main 3270 data structure. */ | 33 | /* The main 3270 data structure. */ |
34 | struct raw3270 { | 34 | struct raw3270 { |
@@ -46,8 +46,6 @@ struct raw3270 { | |||
46 | struct timer_list timer; /* Device timer. */ | 46 | struct timer_list timer; /* Device timer. */ |
47 | 47 | ||
48 | unsigned char *ascebc; /* ascii -> ebcdic table */ | 48 | unsigned char *ascebc; /* ascii -> ebcdic table */ |
49 | struct device *clttydev; /* 3270-class tty device ptr */ | ||
50 | struct device *cltubdev; /* 3270-class tub device ptr */ | ||
51 | 49 | ||
52 | struct raw3270_request init_request; | 50 | struct raw3270_request init_request; |
53 | unsigned char init_data[256]; | 51 | unsigned char init_data[256]; |
@@ -1072,10 +1070,6 @@ raw3270_delete_device(struct raw3270 *rp) | |||
1072 | 1070 | ||
1073 | /* Remove from device chain. */ | 1071 | /* Remove from device chain. */ |
1074 | mutex_lock(&raw3270_mutex); | 1072 | mutex_lock(&raw3270_mutex); |
1075 | if (rp->clttydev && !IS_ERR(rp->clttydev)) | ||
1076 | device_destroy(class3270, MKDEV(IBM_TTY3270_MAJOR, rp->minor)); | ||
1077 | if (rp->cltubdev && !IS_ERR(rp->cltubdev)) | ||
1078 | device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, rp->minor)); | ||
1079 | list_del_init(&rp->list); | 1073 | list_del_init(&rp->list); |
1080 | mutex_unlock(&raw3270_mutex); | 1074 | mutex_unlock(&raw3270_mutex); |
1081 | 1075 | ||
@@ -1139,75 +1133,34 @@ static struct attribute_group raw3270_attr_group = { | |||
1139 | 1133 | ||
1140 | static int raw3270_create_attributes(struct raw3270 *rp) | 1134 | static int raw3270_create_attributes(struct raw3270 *rp) |
1141 | { | 1135 | { |
1142 | int rc; | 1136 | return sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group); |
1143 | |||
1144 | rc = sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group); | ||
1145 | if (rc) | ||
1146 | goto out; | ||
1147 | |||
1148 | rp->clttydev = device_create(class3270, &rp->cdev->dev, | ||
1149 | MKDEV(IBM_TTY3270_MAJOR, rp->minor), NULL, | ||
1150 | "tty%s", dev_name(&rp->cdev->dev)); | ||
1151 | if (IS_ERR(rp->clttydev)) { | ||
1152 | rc = PTR_ERR(rp->clttydev); | ||
1153 | goto out_ttydev; | ||
1154 | } | ||
1155 | |||
1156 | rp->cltubdev = device_create(class3270, &rp->cdev->dev, | ||
1157 | MKDEV(IBM_FS3270_MAJOR, rp->minor), NULL, | ||
1158 | "tub%s", dev_name(&rp->cdev->dev)); | ||
1159 | if (!IS_ERR(rp->cltubdev)) | ||
1160 | goto out; | ||
1161 | |||
1162 | rc = PTR_ERR(rp->cltubdev); | ||
1163 | device_destroy(class3270, MKDEV(IBM_TTY3270_MAJOR, rp->minor)); | ||
1164 | |||
1165 | out_ttydev: | ||
1166 | sysfs_remove_group(&rp->cdev->dev.kobj, &raw3270_attr_group); | ||
1167 | out: | ||
1168 | return rc; | ||
1169 | } | 1137 | } |
1170 | 1138 | ||
1171 | /* | 1139 | /* |
1172 | * Notifier for device addition/removal | 1140 | * Notifier for device addition/removal |
1173 | */ | 1141 | */ |
1174 | struct raw3270_notifier { | ||
1175 | struct list_head list; | ||
1176 | void (*notifier)(int, int); | ||
1177 | }; | ||
1178 | |||
1179 | static LIST_HEAD(raw3270_notifier); | 1142 | static LIST_HEAD(raw3270_notifier); |
1180 | 1143 | ||
1181 | int raw3270_register_notifier(void (*notifier)(int, int)) | 1144 | int raw3270_register_notifier(struct raw3270_notifier *notifier) |
1182 | { | 1145 | { |
1183 | struct raw3270_notifier *np; | ||
1184 | struct raw3270 *rp; | 1146 | struct raw3270 *rp; |
1185 | 1147 | ||
1186 | np = kmalloc(sizeof(struct raw3270_notifier), GFP_KERNEL); | ||
1187 | if (!np) | ||
1188 | return -ENOMEM; | ||
1189 | np->notifier = notifier; | ||
1190 | mutex_lock(&raw3270_mutex); | 1148 | mutex_lock(&raw3270_mutex); |
1191 | list_add_tail(&np->list, &raw3270_notifier); | 1149 | list_add_tail(¬ifier->list, &raw3270_notifier); |
1192 | list_for_each_entry(rp, &raw3270_devices, list) { | 1150 | list_for_each_entry(rp, &raw3270_devices, list) |
1193 | get_device(&rp->cdev->dev); | 1151 | notifier->create(rp->minor); |
1194 | notifier(rp->minor, 1); | ||
1195 | } | ||
1196 | mutex_unlock(&raw3270_mutex); | 1152 | mutex_unlock(&raw3270_mutex); |
1197 | return 0; | 1153 | return 0; |
1198 | } | 1154 | } |
1199 | 1155 | ||
1200 | void raw3270_unregister_notifier(void (*notifier)(int, int)) | 1156 | void raw3270_unregister_notifier(struct raw3270_notifier *notifier) |
1201 | { | 1157 | { |
1202 | struct raw3270_notifier *np; | 1158 | struct raw3270 *rp; |
1203 | 1159 | ||
1204 | mutex_lock(&raw3270_mutex); | 1160 | mutex_lock(&raw3270_mutex); |
1205 | list_for_each_entry(np, &raw3270_notifier, list) | 1161 | list_for_each_entry(rp, &raw3270_devices, list) |
1206 | if (np->notifier == notifier) { | 1162 | notifier->destroy(rp->minor); |
1207 | list_del(&np->list); | 1163 | list_del(¬ifier->list); |
1208 | kfree(np); | ||
1209 | break; | ||
1210 | } | ||
1211 | mutex_unlock(&raw3270_mutex); | 1164 | mutex_unlock(&raw3270_mutex); |
1212 | } | 1165 | } |
1213 | 1166 | ||
@@ -1217,8 +1170,8 @@ void raw3270_unregister_notifier(void (*notifier)(int, int)) | |||
1217 | static int | 1170 | static int |
1218 | raw3270_set_online (struct ccw_device *cdev) | 1171 | raw3270_set_online (struct ccw_device *cdev) |
1219 | { | 1172 | { |
1220 | struct raw3270 *rp; | ||
1221 | struct raw3270_notifier *np; | 1173 | struct raw3270_notifier *np; |
1174 | struct raw3270 *rp; | ||
1222 | int rc; | 1175 | int rc; |
1223 | 1176 | ||
1224 | rp = raw3270_create_device(cdev); | 1177 | rp = raw3270_create_device(cdev); |
@@ -1239,7 +1192,7 @@ raw3270_set_online (struct ccw_device *cdev) | |||
1239 | set_bit(RAW3270_FLAGS_READY, &rp->flags); | 1192 | set_bit(RAW3270_FLAGS_READY, &rp->flags); |
1240 | mutex_lock(&raw3270_mutex); | 1193 | mutex_lock(&raw3270_mutex); |
1241 | list_for_each_entry(np, &raw3270_notifier, list) | 1194 | list_for_each_entry(np, &raw3270_notifier, list) |
1242 | np->notifier(rp->minor, 1); | 1195 | np->create(rp->minor); |
1243 | mutex_unlock(&raw3270_mutex); | 1196 | mutex_unlock(&raw3270_mutex); |
1244 | return 0; | 1197 | return 0; |
1245 | 1198 | ||
@@ -1290,7 +1243,7 @@ raw3270_remove (struct ccw_device *cdev) | |||
1290 | 1243 | ||
1291 | mutex_lock(&raw3270_mutex); | 1244 | mutex_lock(&raw3270_mutex); |
1292 | list_for_each_entry(np, &raw3270_notifier, list) | 1245 | list_for_each_entry(np, &raw3270_notifier, list) |
1293 | np->notifier(rp->minor, 0); | 1246 | np->destroy(rp->minor); |
1294 | mutex_unlock(&raw3270_mutex); | 1247 | mutex_unlock(&raw3270_mutex); |
1295 | 1248 | ||
1296 | /* Reset 3270 device. */ | 1249 | /* Reset 3270 device. */ |
@@ -1434,6 +1387,7 @@ MODULE_LICENSE("GPL"); | |||
1434 | module_init(raw3270_init); | 1387 | module_init(raw3270_init); |
1435 | module_exit(raw3270_exit); | 1388 | module_exit(raw3270_exit); |
1436 | 1389 | ||
1390 | EXPORT_SYMBOL(class3270); | ||
1437 | EXPORT_SYMBOL(raw3270_request_alloc); | 1391 | EXPORT_SYMBOL(raw3270_request_alloc); |
1438 | EXPORT_SYMBOL(raw3270_request_free); | 1392 | EXPORT_SYMBOL(raw3270_request_free); |
1439 | EXPORT_SYMBOL(raw3270_request_reset); | 1393 | EXPORT_SYMBOL(raw3270_request_reset); |
diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index ed34eb2199cc..a4c79d043cd3 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h | |||
@@ -91,6 +91,7 @@ struct raw3270_iocb { | |||
91 | 91 | ||
92 | struct raw3270; | 92 | struct raw3270; |
93 | struct raw3270_view; | 93 | struct raw3270_view; |
94 | extern struct class *class3270; | ||
94 | 95 | ||
95 | /* 3270 CCW request */ | 96 | /* 3270 CCW request */ |
96 | struct raw3270_request { | 97 | struct raw3270_request { |
@@ -192,8 +193,14 @@ struct raw3270 *raw3270_setup_console(struct ccw_device *cdev); | |||
192 | void raw3270_wait_cons_dev(struct raw3270 *); | 193 | void raw3270_wait_cons_dev(struct raw3270 *); |
193 | 194 | ||
194 | /* Notifier for device addition/removal */ | 195 | /* Notifier for device addition/removal */ |
195 | int raw3270_register_notifier(void (*notifier)(int, int)); | 196 | struct raw3270_notifier { |
196 | void raw3270_unregister_notifier(void (*notifier)(int, int)); | 197 | struct list_head list; |
198 | void (*create)(int minor); | ||
199 | void (*destroy)(int minor); | ||
200 | }; | ||
201 | |||
202 | int raw3270_register_notifier(struct raw3270_notifier *); | ||
203 | void raw3270_unregister_notifier(struct raw3270_notifier *); | ||
197 | void raw3270_pm_unfreeze(struct raw3270_view *); | 204 | void raw3270_pm_unfreeze(struct raw3270_view *); |
198 | 205 | ||
199 | /* | 206 | /* |
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 5e4b6fc49ae3..48767e6bab9d 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c | |||
@@ -829,9 +829,8 @@ tty3270_del_views(void) | |||
829 | { | 829 | { |
830 | int i; | 830 | int i; |
831 | 831 | ||
832 | for (i = 0; i < tty3270_max_index; i++) { | 832 | for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) { |
833 | struct raw3270_view *view = | 833 | struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i); |
834 | raw3270_find_view(&tty3270_fn, i + RAW3270_FIRSTMINOR); | ||
835 | if (!IS_ERR(view)) | 834 | if (!IS_ERR(view)) |
836 | raw3270_del_view(view); | 835 | raw3270_del_view(view); |
837 | } | 836 | } |
@@ -855,8 +854,7 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) | |||
855 | int i, rc; | 854 | int i, rc; |
856 | 855 | ||
857 | /* Check if the tty3270 is already there. */ | 856 | /* Check if the tty3270 is already there. */ |
858 | view = raw3270_find_view(&tty3270_fn, | 857 | view = raw3270_find_view(&tty3270_fn, tty->index); |
859 | tty->index + RAW3270_FIRSTMINOR); | ||
860 | if (!IS_ERR(view)) { | 858 | if (!IS_ERR(view)) { |
861 | tp = container_of(view, struct tty3270, view); | 859 | tp = container_of(view, struct tty3270, view); |
862 | tty->driver_data = tp; | 860 | tty->driver_data = tp; |
@@ -868,8 +866,8 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) | |||
868 | tp->inattr = TF_INPUT; | 866 | tp->inattr = TF_INPUT; |
869 | return tty_port_install(&tp->port, driver, tty); | 867 | return tty_port_install(&tp->port, driver, tty); |
870 | } | 868 | } |
871 | if (tty3270_max_index < tty->index + 1) | 869 | if (tty3270_max_index < tty->index) |
872 | tty3270_max_index = tty->index + 1; | 870 | tty3270_max_index = tty->index; |
873 | 871 | ||
874 | /* Quick exit if there is no device for tty->index. */ | 872 | /* Quick exit if there is no device for tty->index. */ |
875 | if (PTR_ERR(view) == -ENODEV) | 873 | if (PTR_ERR(view) == -ENODEV) |
@@ -880,8 +878,7 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) | |||
880 | if (IS_ERR(tp)) | 878 | if (IS_ERR(tp)) |
881 | return PTR_ERR(tp); | 879 | return PTR_ERR(tp); |
882 | 880 | ||
883 | rc = raw3270_add_view(&tp->view, &tty3270_fn, | 881 | rc = raw3270_add_view(&tp->view, &tty3270_fn, tty->index); |
884 | tty->index + RAW3270_FIRSTMINOR); | ||
885 | if (rc) { | 882 | if (rc) { |
886 | tty3270_free_view(tp); | 883 | tty3270_free_view(tp); |
887 | return rc; | 884 | return rc; |
@@ -1788,6 +1785,22 @@ static const struct tty_operations tty3270_ops = { | |||
1788 | .set_termios = tty3270_set_termios | 1785 | .set_termios = tty3270_set_termios |
1789 | }; | 1786 | }; |
1790 | 1787 | ||
1788 | void tty3270_create_cb(int minor) | ||
1789 | { | ||
1790 | tty_register_device(tty3270_driver, minor, NULL); | ||
1791 | } | ||
1792 | |||
1793 | void tty3270_destroy_cb(int minor) | ||
1794 | { | ||
1795 | tty_unregister_device(tty3270_driver, minor); | ||
1796 | } | ||
1797 | |||
1798 | struct raw3270_notifier tty3270_notifier = | ||
1799 | { | ||
1800 | .create = tty3270_create_cb, | ||
1801 | .destroy = tty3270_destroy_cb, | ||
1802 | }; | ||
1803 | |||
1791 | /* | 1804 | /* |
1792 | * 3270 tty registration code called from tty_init(). | 1805 | * 3270 tty registration code called from tty_init(). |
1793 | * Most kernel services (incl. kmalloc) are available at this poimt. | 1806 | * Most kernel services (incl. kmalloc) are available at this poimt. |
@@ -1797,23 +1810,25 @@ static int __init tty3270_init(void) | |||
1797 | struct tty_driver *driver; | 1810 | struct tty_driver *driver; |
1798 | int ret; | 1811 | int ret; |
1799 | 1812 | ||
1800 | driver = alloc_tty_driver(RAW3270_MAXDEVS); | 1813 | driver = tty_alloc_driver(RAW3270_MAXDEVS, |
1801 | if (!driver) | 1814 | TTY_DRIVER_REAL_RAW | |
1802 | return -ENOMEM; | 1815 | TTY_DRIVER_DYNAMIC_DEV | |
1816 | TTY_DRIVER_RESET_TERMIOS); | ||
1817 | if (IS_ERR(driver)) | ||
1818 | return PTR_ERR(driver); | ||
1803 | 1819 | ||
1804 | /* | 1820 | /* |
1805 | * Initialize the tty_driver structure | 1821 | * Initialize the tty_driver structure |
1806 | * Entries in tty3270_driver that are NOT initialized: | 1822 | * Entries in tty3270_driver that are NOT initialized: |
1807 | * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc | 1823 | * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc |
1808 | */ | 1824 | */ |
1809 | driver->driver_name = "ttyTUB"; | 1825 | driver->driver_name = "tty3270"; |
1810 | driver->name = "ttyTUB"; | 1826 | driver->name = "3270/tty"; |
1811 | driver->major = IBM_TTY3270_MAJOR; | 1827 | driver->major = IBM_TTY3270_MAJOR; |
1812 | driver->minor_start = RAW3270_FIRSTMINOR; | 1828 | driver->minor_start = 0; |
1813 | driver->type = TTY_DRIVER_TYPE_SYSTEM; | 1829 | driver->type = TTY_DRIVER_TYPE_SYSTEM; |
1814 | driver->subtype = SYSTEM_TYPE_TTY; | 1830 | driver->subtype = SYSTEM_TYPE_TTY; |
1815 | driver->init_termios = tty_std_termios; | 1831 | driver->init_termios = tty_std_termios; |
1816 | driver->flags = TTY_DRIVER_RESET_TERMIOS; | ||
1817 | tty_set_operations(driver, &tty3270_ops); | 1832 | tty_set_operations(driver, &tty3270_ops); |
1818 | ret = tty_register_driver(driver); | 1833 | ret = tty_register_driver(driver); |
1819 | if (ret) { | 1834 | if (ret) { |
@@ -1821,6 +1836,7 @@ static int __init tty3270_init(void) | |||
1821 | return ret; | 1836 | return ret; |
1822 | } | 1837 | } |
1823 | tty3270_driver = driver; | 1838 | tty3270_driver = driver; |
1839 | raw3270_register_notifier(&tty3270_notifier); | ||
1824 | return 0; | 1840 | return 0; |
1825 | } | 1841 | } |
1826 | 1842 | ||
@@ -1829,6 +1845,7 @@ tty3270_exit(void) | |||
1829 | { | 1845 | { |
1830 | struct tty_driver *driver; | 1846 | struct tty_driver *driver; |
1831 | 1847 | ||
1848 | raw3270_unregister_notifier(&tty3270_notifier); | ||
1832 | driver = tty3270_driver; | 1849 | driver = tty3270_driver; |
1833 | tty3270_driver = NULL; | 1850 | tty3270_driver = NULL; |
1834 | tty_unregister_driver(driver); | 1851 | tty_unregister_driver(driver); |