diff options
Diffstat (limited to 'drivers/char/vt_ioctl.c')
-rw-r--r-- | drivers/char/vt_ioctl.c | 484 |
1 files changed, 433 insertions, 51 deletions
diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 95189f288f8c..6aa10284104a 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c | |||
@@ -16,6 +16,8 @@ | |||
16 | #include <linux/tty.h> | 16 | #include <linux/tty.h> |
17 | #include <linux/timer.h> | 17 | #include <linux/timer.h> |
18 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
19 | #include <linux/compat.h> | ||
20 | #include <linux/module.h> | ||
19 | #include <linux/kd.h> | 21 | #include <linux/kd.h> |
20 | #include <linux/vt.h> | 22 | #include <linux/vt.h> |
21 | #include <linux/string.h> | 23 | #include <linux/string.h> |
@@ -62,6 +64,133 @@ extern struct tty_driver *console_driver; | |||
62 | static void complete_change_console(struct vc_data *vc); | 64 | static void complete_change_console(struct vc_data *vc); |
63 | 65 | ||
64 | /* | 66 | /* |
67 | * User space VT_EVENT handlers | ||
68 | */ | ||
69 | |||
70 | struct vt_event_wait { | ||
71 | struct list_head list; | ||
72 | struct vt_event event; | ||
73 | int done; | ||
74 | }; | ||
75 | |||
76 | static LIST_HEAD(vt_events); | ||
77 | static DEFINE_SPINLOCK(vt_event_lock); | ||
78 | static DECLARE_WAIT_QUEUE_HEAD(vt_event_waitqueue); | ||
79 | |||
80 | /** | ||
81 | * vt_event_post | ||
82 | * @event: the event that occurred | ||
83 | * @old: old console | ||
84 | * @new: new console | ||
85 | * | ||
86 | * Post an VT event to interested VT handlers | ||
87 | */ | ||
88 | |||
89 | void vt_event_post(unsigned int event, unsigned int old, unsigned int new) | ||
90 | { | ||
91 | struct list_head *pos, *head; | ||
92 | unsigned long flags; | ||
93 | int wake = 0; | ||
94 | |||
95 | spin_lock_irqsave(&vt_event_lock, flags); | ||
96 | head = &vt_events; | ||
97 | |||
98 | list_for_each(pos, head) { | ||
99 | struct vt_event_wait *ve = list_entry(pos, | ||
100 | struct vt_event_wait, list); | ||
101 | if (!(ve->event.event & event)) | ||
102 | continue; | ||
103 | ve->event.event = event; | ||
104 | /* kernel view is consoles 0..n-1, user space view is | ||
105 | console 1..n with 0 meaning current, so we must bias */ | ||
106 | ve->event.oldev = old + 1; | ||
107 | ve->event.newev = new + 1; | ||
108 | wake = 1; | ||
109 | ve->done = 1; | ||
110 | } | ||
111 | spin_unlock_irqrestore(&vt_event_lock, flags); | ||
112 | if (wake) | ||
113 | wake_up_interruptible(&vt_event_waitqueue); | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * vt_event_wait - wait for an event | ||
118 | * @vw: our event | ||
119 | * | ||
120 | * Waits for an event to occur which completes our vt_event_wait | ||
121 | * structure. On return the structure has wv->done set to 1 for success | ||
122 | * or 0 if some event such as a signal ended the wait. | ||
123 | */ | ||
124 | |||
125 | static void vt_event_wait(struct vt_event_wait *vw) | ||
126 | { | ||
127 | unsigned long flags; | ||
128 | /* Prepare the event */ | ||
129 | INIT_LIST_HEAD(&vw->list); | ||
130 | vw->done = 0; | ||
131 | /* Queue our event */ | ||
132 | spin_lock_irqsave(&vt_event_lock, flags); | ||
133 | list_add(&vw->list, &vt_events); | ||
134 | spin_unlock_irqrestore(&vt_event_lock, flags); | ||
135 | /* Wait for it to pass */ | ||
136 | wait_event_interruptible(vt_event_waitqueue, vw->done); | ||
137 | /* Dequeue it */ | ||
138 | spin_lock_irqsave(&vt_event_lock, flags); | ||
139 | list_del(&vw->list); | ||
140 | spin_unlock_irqrestore(&vt_event_lock, flags); | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * vt_event_wait_ioctl - event ioctl handler | ||
145 | * @arg: argument to ioctl | ||
146 | * | ||
147 | * Implement the VT_WAITEVENT ioctl using the VT event interface | ||
148 | */ | ||
149 | |||
150 | static int vt_event_wait_ioctl(struct vt_event __user *event) | ||
151 | { | ||
152 | struct vt_event_wait vw; | ||
153 | |||
154 | if (copy_from_user(&vw.event, event, sizeof(struct vt_event))) | ||
155 | return -EFAULT; | ||
156 | /* Highest supported event for now */ | ||
157 | if (vw.event.event & ~VT_MAX_EVENT) | ||
158 | return -EINVAL; | ||
159 | |||
160 | vt_event_wait(&vw); | ||
161 | /* If it occurred report it */ | ||
162 | if (vw.done) { | ||
163 | if (copy_to_user(event, &vw.event, sizeof(struct vt_event))) | ||
164 | return -EFAULT; | ||
165 | return 0; | ||
166 | } | ||
167 | return -EINTR; | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * vt_waitactive - active console wait | ||
172 | * @event: event code | ||
173 | * @n: new console | ||
174 | * | ||
175 | * Helper for event waits. Used to implement the legacy | ||
176 | * event waiting ioctls in terms of events | ||
177 | */ | ||
178 | |||
179 | int vt_waitactive(int n) | ||
180 | { | ||
181 | struct vt_event_wait vw; | ||
182 | do { | ||
183 | if (n == fg_console + 1) | ||
184 | break; | ||
185 | vw.event.event = VT_EVENT_SWITCH; | ||
186 | vt_event_wait(&vw); | ||
187 | if (vw.done == 0) | ||
188 | return -EINTR; | ||
189 | } while (vw.event.newev != n); | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | /* | ||
65 | * these are the valid i/o ports we're allowed to change. they map all the | 194 | * these are the valid i/o ports we're allowed to change. they map all the |
66 | * video ports | 195 | * video ports |
67 | */ | 196 | */ |
@@ -360,6 +489,8 @@ do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_ | |||
360 | return 0; | 489 | return 0; |
361 | } | 490 | } |
362 | 491 | ||
492 | |||
493 | |||
363 | /* | 494 | /* |
364 | * We handle the console-specific ioctl's here. We allow the | 495 | * We handle the console-specific ioctl's here. We allow the |
365 | * capability to modify any console, not just the fg_console. | 496 | * capability to modify any console, not just the fg_console. |
@@ -842,6 +973,43 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, | |||
842 | } | 973 | } |
843 | break; | 974 | break; |
844 | 975 | ||
976 | case VT_SETACTIVATE: | ||
977 | { | ||
978 | struct vt_setactivate vsa; | ||
979 | |||
980 | if (!perm) | ||
981 | goto eperm; | ||
982 | |||
983 | if (copy_from_user(&vsa, (struct vt_setactivate __user *)arg, | ||
984 | sizeof(struct vt_setactivate))) { | ||
985 | ret = -EFAULT; | ||
986 | goto out; | ||
987 | } | ||
988 | if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES) | ||
989 | ret = -ENXIO; | ||
990 | else { | ||
991 | vsa.console--; | ||
992 | acquire_console_sem(); | ||
993 | ret = vc_allocate(vsa.console); | ||
994 | if (ret == 0) { | ||
995 | struct vc_data *nvc; | ||
996 | /* This is safe providing we don't drop the | ||
997 | console sem between vc_allocate and | ||
998 | finishing referencing nvc */ | ||
999 | nvc = vc_cons[vsa.console].d; | ||
1000 | nvc->vt_mode = vsa.mode; | ||
1001 | nvc->vt_mode.frsig = 0; | ||
1002 | put_pid(nvc->vt_pid); | ||
1003 | nvc->vt_pid = get_pid(task_pid(current)); | ||
1004 | } | ||
1005 | release_console_sem(); | ||
1006 | if (ret) | ||
1007 | break; | ||
1008 | /* Commence switch and lock */ | ||
1009 | set_console(arg); | ||
1010 | } | ||
1011 | } | ||
1012 | |||
845 | /* | 1013 | /* |
846 | * wait until the specified VT has been activated | 1014 | * wait until the specified VT has been activated |
847 | */ | 1015 | */ |
@@ -851,7 +1019,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, | |||
851 | if (arg == 0 || arg > MAX_NR_CONSOLES) | 1019 | if (arg == 0 || arg > MAX_NR_CONSOLES) |
852 | ret = -ENXIO; | 1020 | ret = -ENXIO; |
853 | else | 1021 | else |
854 | ret = vt_waitactive(arg - 1); | 1022 | ret = vt_waitactive(arg); |
855 | break; | 1023 | break; |
856 | 1024 | ||
857 | /* | 1025 | /* |
@@ -1159,6 +1327,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, | |||
1159 | ret = put_user(vc->vc_hi_font_mask, | 1327 | ret = put_user(vc->vc_hi_font_mask, |
1160 | (unsigned short __user *)arg); | 1328 | (unsigned short __user *)arg); |
1161 | break; | 1329 | break; |
1330 | case VT_WAITEVENT: | ||
1331 | ret = vt_event_wait_ioctl((struct vt_event __user *)arg); | ||
1332 | break; | ||
1162 | default: | 1333 | default: |
1163 | ret = -ENOIOCTLCMD; | 1334 | ret = -ENOIOCTLCMD; |
1164 | } | 1335 | } |
@@ -1170,54 +1341,6 @@ eperm: | |||
1170 | goto out; | 1341 | goto out; |
1171 | } | 1342 | } |
1172 | 1343 | ||
1173 | /* | ||
1174 | * Sometimes we want to wait until a particular VT has been activated. We | ||
1175 | * do it in a very simple manner. Everybody waits on a single queue and | ||
1176 | * get woken up at once. Those that are satisfied go on with their business, | ||
1177 | * while those not ready go back to sleep. Seems overkill to add a wait | ||
1178 | * to each vt just for this - usually this does nothing! | ||
1179 | */ | ||
1180 | static DECLARE_WAIT_QUEUE_HEAD(vt_activate_queue); | ||
1181 | |||
1182 | /* | ||
1183 | * Sleeps until a vt is activated, or the task is interrupted. Returns | ||
1184 | * 0 if activation, -EINTR if interrupted by a signal handler. | ||
1185 | */ | ||
1186 | int vt_waitactive(int vt) | ||
1187 | { | ||
1188 | int retval; | ||
1189 | DECLARE_WAITQUEUE(wait, current); | ||
1190 | |||
1191 | add_wait_queue(&vt_activate_queue, &wait); | ||
1192 | for (;;) { | ||
1193 | retval = 0; | ||
1194 | |||
1195 | /* | ||
1196 | * Synchronize with redraw_screen(). By acquiring the console | ||
1197 | * semaphore we make sure that the console switch is completed | ||
1198 | * before we return. If we didn't wait for the semaphore, we | ||
1199 | * could return at a point where fg_console has already been | ||
1200 | * updated, but the console switch hasn't been completed. | ||
1201 | */ | ||
1202 | acquire_console_sem(); | ||
1203 | set_current_state(TASK_INTERRUPTIBLE); | ||
1204 | if (vt == fg_console) { | ||
1205 | release_console_sem(); | ||
1206 | break; | ||
1207 | } | ||
1208 | release_console_sem(); | ||
1209 | retval = -ERESTARTNOHAND; | ||
1210 | if (signal_pending(current)) | ||
1211 | break; | ||
1212 | schedule(); | ||
1213 | } | ||
1214 | remove_wait_queue(&vt_activate_queue, &wait); | ||
1215 | __set_current_state(TASK_RUNNING); | ||
1216 | return retval; | ||
1217 | } | ||
1218 | |||
1219 | #define vt_wake_waitactive() wake_up(&vt_activate_queue) | ||
1220 | |||
1221 | void reset_vc(struct vc_data *vc) | 1344 | void reset_vc(struct vc_data *vc) |
1222 | { | 1345 | { |
1223 | vc->vc_mode = KD_TEXT; | 1346 | vc->vc_mode = KD_TEXT; |
@@ -1256,12 +1379,216 @@ void vc_SAK(struct work_struct *work) | |||
1256 | release_console_sem(); | 1379 | release_console_sem(); |
1257 | } | 1380 | } |
1258 | 1381 | ||
1382 | #ifdef CONFIG_COMPAT | ||
1383 | |||
1384 | struct compat_consolefontdesc { | ||
1385 | unsigned short charcount; /* characters in font (256 or 512) */ | ||
1386 | unsigned short charheight; /* scan lines per character (1-32) */ | ||
1387 | compat_caddr_t chardata; /* font data in expanded form */ | ||
1388 | }; | ||
1389 | |||
1390 | static inline int | ||
1391 | compat_fontx_ioctl(int cmd, struct compat_consolefontdesc __user *user_cfd, | ||
1392 | int perm, struct console_font_op *op) | ||
1393 | { | ||
1394 | struct compat_consolefontdesc cfdarg; | ||
1395 | int i; | ||
1396 | |||
1397 | if (copy_from_user(&cfdarg, user_cfd, sizeof(struct compat_consolefontdesc))) | ||
1398 | return -EFAULT; | ||
1399 | |||
1400 | switch (cmd) { | ||
1401 | case PIO_FONTX: | ||
1402 | if (!perm) | ||
1403 | return -EPERM; | ||
1404 | op->op = KD_FONT_OP_SET; | ||
1405 | op->flags = KD_FONT_FLAG_OLD; | ||
1406 | op->width = 8; | ||
1407 | op->height = cfdarg.charheight; | ||
1408 | op->charcount = cfdarg.charcount; | ||
1409 | op->data = compat_ptr(cfdarg.chardata); | ||
1410 | return con_font_op(vc_cons[fg_console].d, op); | ||
1411 | case GIO_FONTX: | ||
1412 | op->op = KD_FONT_OP_GET; | ||
1413 | op->flags = KD_FONT_FLAG_OLD; | ||
1414 | op->width = 8; | ||
1415 | op->height = cfdarg.charheight; | ||
1416 | op->charcount = cfdarg.charcount; | ||
1417 | op->data = compat_ptr(cfdarg.chardata); | ||
1418 | i = con_font_op(vc_cons[fg_console].d, op); | ||
1419 | if (i) | ||
1420 | return i; | ||
1421 | cfdarg.charheight = op->height; | ||
1422 | cfdarg.charcount = op->charcount; | ||
1423 | if (copy_to_user(user_cfd, &cfdarg, sizeof(struct compat_consolefontdesc))) | ||
1424 | return -EFAULT; | ||
1425 | return 0; | ||
1426 | } | ||
1427 | return -EINVAL; | ||
1428 | } | ||
1429 | |||
1430 | struct compat_console_font_op { | ||
1431 | compat_uint_t op; /* operation code KD_FONT_OP_* */ | ||
1432 | compat_uint_t flags; /* KD_FONT_FLAG_* */ | ||
1433 | compat_uint_t width, height; /* font size */ | ||
1434 | compat_uint_t charcount; | ||
1435 | compat_caddr_t data; /* font data with height fixed to 32 */ | ||
1436 | }; | ||
1437 | |||
1438 | static inline int | ||
1439 | compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop, | ||
1440 | int perm, struct console_font_op *op, struct vc_data *vc) | ||
1441 | { | ||
1442 | int i; | ||
1443 | |||
1444 | if (copy_from_user(op, fontop, sizeof(struct compat_console_font_op))) | ||
1445 | return -EFAULT; | ||
1446 | if (!perm && op->op != KD_FONT_OP_GET) | ||
1447 | return -EPERM; | ||
1448 | op->data = compat_ptr(((struct compat_console_font_op *)op)->data); | ||
1449 | op->flags |= KD_FONT_FLAG_OLD; | ||
1450 | i = con_font_op(vc, op); | ||
1451 | if (i) | ||
1452 | return i; | ||
1453 | ((struct compat_console_font_op *)op)->data = (unsigned long)op->data; | ||
1454 | if (copy_to_user(fontop, op, sizeof(struct compat_console_font_op))) | ||
1455 | return -EFAULT; | ||
1456 | return 0; | ||
1457 | } | ||
1458 | |||
1459 | struct compat_unimapdesc { | ||
1460 | unsigned short entry_ct; | ||
1461 | compat_caddr_t entries; | ||
1462 | }; | ||
1463 | |||
1464 | static inline int | ||
1465 | compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud, | ||
1466 | int perm, struct vc_data *vc) | ||
1467 | { | ||
1468 | struct compat_unimapdesc tmp; | ||
1469 | struct unipair __user *tmp_entries; | ||
1470 | |||
1471 | if (copy_from_user(&tmp, user_ud, sizeof tmp)) | ||
1472 | return -EFAULT; | ||
1473 | tmp_entries = compat_ptr(tmp.entries); | ||
1474 | if (tmp_entries) | ||
1475 | if (!access_ok(VERIFY_WRITE, tmp_entries, | ||
1476 | tmp.entry_ct*sizeof(struct unipair))) | ||
1477 | return -EFAULT; | ||
1478 | switch (cmd) { | ||
1479 | case PIO_UNIMAP: | ||
1480 | if (!perm) | ||
1481 | return -EPERM; | ||
1482 | return con_set_unimap(vc, tmp.entry_ct, tmp_entries); | ||
1483 | case GIO_UNIMAP: | ||
1484 | if (!perm && fg_console != vc->vc_num) | ||
1485 | return -EPERM; | ||
1486 | return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp_entries); | ||
1487 | } | ||
1488 | return 0; | ||
1489 | } | ||
1490 | |||
1491 | long vt_compat_ioctl(struct tty_struct *tty, struct file * file, | ||
1492 | unsigned int cmd, unsigned long arg) | ||
1493 | { | ||
1494 | struct vc_data *vc = tty->driver_data; | ||
1495 | struct console_font_op op; /* used in multiple places here */ | ||
1496 | struct kbd_struct *kbd; | ||
1497 | unsigned int console; | ||
1498 | void __user *up = (void __user *)arg; | ||
1499 | int perm; | ||
1500 | int ret = 0; | ||
1501 | |||
1502 | console = vc->vc_num; | ||
1503 | |||
1504 | lock_kernel(); | ||
1505 | |||
1506 | if (!vc_cons_allocated(console)) { /* impossible? */ | ||
1507 | ret = -ENOIOCTLCMD; | ||
1508 | goto out; | ||
1509 | } | ||
1510 | |||
1511 | /* | ||
1512 | * To have permissions to do most of the vt ioctls, we either have | ||
1513 | * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. | ||
1514 | */ | ||
1515 | perm = 0; | ||
1516 | if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) | ||
1517 | perm = 1; | ||
1518 | |||
1519 | kbd = kbd_table + console; | ||
1520 | switch (cmd) { | ||
1521 | /* | ||
1522 | * these need special handlers for incompatible data structures | ||
1523 | */ | ||
1524 | case PIO_FONTX: | ||
1525 | case GIO_FONTX: | ||
1526 | ret = compat_fontx_ioctl(cmd, up, perm, &op); | ||
1527 | break; | ||
1528 | |||
1529 | case KDFONTOP: | ||
1530 | ret = compat_kdfontop_ioctl(up, perm, &op, vc); | ||
1531 | break; | ||
1532 | |||
1533 | case PIO_UNIMAP: | ||
1534 | case GIO_UNIMAP: | ||
1535 | ret = compat_unimap_ioctl(cmd, up, perm, vc); | ||
1536 | break; | ||
1537 | |||
1538 | /* | ||
1539 | * all these treat 'arg' as an integer | ||
1540 | */ | ||
1541 | case KIOCSOUND: | ||
1542 | case KDMKTONE: | ||
1543 | #ifdef CONFIG_X86 | ||
1544 | case KDADDIO: | ||
1545 | case KDDELIO: | ||
1546 | #endif | ||
1547 | case KDSETMODE: | ||
1548 | case KDMAPDISP: | ||
1549 | case KDUNMAPDISP: | ||
1550 | case KDSKBMODE: | ||
1551 | case KDSKBMETA: | ||
1552 | case KDSKBLED: | ||
1553 | case KDSETLED: | ||
1554 | case KDSIGACCEPT: | ||
1555 | case VT_ACTIVATE: | ||
1556 | case VT_WAITACTIVE: | ||
1557 | case VT_RELDISP: | ||
1558 | case VT_DISALLOCATE: | ||
1559 | case VT_RESIZE: | ||
1560 | case VT_RESIZEX: | ||
1561 | goto fallback; | ||
1562 | |||
1563 | /* | ||
1564 | * the rest has a compatible data structure behind arg, | ||
1565 | * but we have to convert it to a proper 64 bit pointer. | ||
1566 | */ | ||
1567 | default: | ||
1568 | arg = (unsigned long)compat_ptr(arg); | ||
1569 | goto fallback; | ||
1570 | } | ||
1571 | out: | ||
1572 | unlock_kernel(); | ||
1573 | return ret; | ||
1574 | |||
1575 | fallback: | ||
1576 | unlock_kernel(); | ||
1577 | return vt_ioctl(tty, file, cmd, arg); | ||
1578 | } | ||
1579 | |||
1580 | |||
1581 | #endif /* CONFIG_COMPAT */ | ||
1582 | |||
1583 | |||
1259 | /* | 1584 | /* |
1260 | * Performs the back end of a vt switch | 1585 | * Performs the back end of a vt switch. Called under the console |
1586 | * semaphore. | ||
1261 | */ | 1587 | */ |
1262 | static void complete_change_console(struct vc_data *vc) | 1588 | static void complete_change_console(struct vc_data *vc) |
1263 | { | 1589 | { |
1264 | unsigned char old_vc_mode; | 1590 | unsigned char old_vc_mode; |
1591 | int old = fg_console; | ||
1265 | 1592 | ||
1266 | last_console = fg_console; | 1593 | last_console = fg_console; |
1267 | 1594 | ||
@@ -1325,7 +1652,7 @@ static void complete_change_console(struct vc_data *vc) | |||
1325 | /* | 1652 | /* |
1326 | * Wake anyone waiting for their VT to activate | 1653 | * Wake anyone waiting for their VT to activate |
1327 | */ | 1654 | */ |
1328 | vt_wake_waitactive(); | 1655 | vt_event_post(VT_EVENT_SWITCH, old, vc->vc_num); |
1329 | return; | 1656 | return; |
1330 | } | 1657 | } |
1331 | 1658 | ||
@@ -1398,3 +1725,58 @@ void change_console(struct vc_data *new_vc) | |||
1398 | 1725 | ||
1399 | complete_change_console(new_vc); | 1726 | complete_change_console(new_vc); |
1400 | } | 1727 | } |
1728 | |||
1729 | /* Perform a kernel triggered VT switch for suspend/resume */ | ||
1730 | |||
1731 | static int disable_vt_switch; | ||
1732 | |||
1733 | int vt_move_to_console(unsigned int vt, int alloc) | ||
1734 | { | ||
1735 | int prev; | ||
1736 | |||
1737 | acquire_console_sem(); | ||
1738 | /* Graphics mode - up to X */ | ||
1739 | if (disable_vt_switch) { | ||
1740 | release_console_sem(); | ||
1741 | return 0; | ||
1742 | } | ||
1743 | prev = fg_console; | ||
1744 | |||
1745 | if (alloc && vc_allocate(vt)) { | ||
1746 | /* we can't have a free VC for now. Too bad, | ||
1747 | * we don't want to mess the screen for now. */ | ||
1748 | release_console_sem(); | ||
1749 | return -ENOSPC; | ||
1750 | } | ||
1751 | |||
1752 | if (set_console(vt)) { | ||
1753 | /* | ||
1754 | * We're unable to switch to the SUSPEND_CONSOLE. | ||
1755 | * Let the calling function know so it can decide | ||
1756 | * what to do. | ||
1757 | */ | ||
1758 | release_console_sem(); | ||
1759 | return -EIO; | ||
1760 | } | ||
1761 | release_console_sem(); | ||
1762 | if (vt_waitactive(vt + 1)) { | ||
1763 | pr_debug("Suspend: Can't switch VCs."); | ||
1764 | return -EINTR; | ||
1765 | } | ||
1766 | return prev; | ||
1767 | } | ||
1768 | |||
1769 | /* | ||
1770 | * Normally during a suspend, we allocate a new console and switch to it. | ||
1771 | * When we resume, we switch back to the original console. This switch | ||
1772 | * can be slow, so on systems where the framebuffer can handle restoration | ||
1773 | * of video registers anyways, there's little point in doing the console | ||
1774 | * switch. This function allows you to disable it by passing it '0'. | ||
1775 | */ | ||
1776 | void pm_set_vt_switch(int do_switch) | ||
1777 | { | ||
1778 | acquire_console_sem(); | ||
1779 | disable_vt_switch = !do_switch; | ||
1780 | release_console_sem(); | ||
1781 | } | ||
1782 | EXPORT_SYMBOL(pm_set_vt_switch); | ||