diff options
Diffstat (limited to 'drivers/char/vt_ioctl.c')
-rw-r--r-- | drivers/char/vt_ioctl.c | 185 |
1 files changed, 135 insertions, 50 deletions
diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 95189f288f8c..35ad94e65d0d 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c | |||
@@ -62,6 +62,133 @@ extern struct tty_driver *console_driver; | |||
62 | static void complete_change_console(struct vc_data *vc); | 62 | static void complete_change_console(struct vc_data *vc); |
63 | 63 | ||
64 | /* | 64 | /* |
65 | * User space VT_EVENT handlers | ||
66 | */ | ||
67 | |||
68 | struct vt_event_wait { | ||
69 | struct list_head list; | ||
70 | struct vt_event event; | ||
71 | int done; | ||
72 | }; | ||
73 | |||
74 | static LIST_HEAD(vt_events); | ||
75 | static DEFINE_SPINLOCK(vt_event_lock); | ||
76 | static DECLARE_WAIT_QUEUE_HEAD(vt_event_waitqueue); | ||
77 | |||
78 | /** | ||
79 | * vt_event_post | ||
80 | * @event: the event that occurred | ||
81 | * @old: old console | ||
82 | * @new: new console | ||
83 | * | ||
84 | * Post an VT event to interested VT handlers | ||
85 | */ | ||
86 | |||
87 | void vt_event_post(unsigned int event, unsigned int old, unsigned int new) | ||
88 | { | ||
89 | struct list_head *pos, *head; | ||
90 | unsigned long flags; | ||
91 | int wake = 0; | ||
92 | |||
93 | spin_lock_irqsave(&vt_event_lock, flags); | ||
94 | head = &vt_events; | ||
95 | |||
96 | list_for_each(pos, head) { | ||
97 | struct vt_event_wait *ve = list_entry(pos, | ||
98 | struct vt_event_wait, list); | ||
99 | if (!(ve->event.event & event)) | ||
100 | continue; | ||
101 | ve->event.event = event; | ||
102 | /* kernel view is consoles 0..n-1, user space view is | ||
103 | console 1..n with 0 meaning current, so we must bias */ | ||
104 | ve->event.old = old + 1; | ||
105 | ve->event.new = new + 1; | ||
106 | wake = 1; | ||
107 | ve->done = 1; | ||
108 | } | ||
109 | spin_unlock_irqrestore(&vt_event_lock, flags); | ||
110 | if (wake) | ||
111 | wake_up_interruptible(&vt_event_waitqueue); | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * vt_event_wait - wait for an event | ||
116 | * @vw: our event | ||
117 | * | ||
118 | * Waits for an event to occur which completes our vt_event_wait | ||
119 | * structure. On return the structure has wv->done set to 1 for success | ||
120 | * or 0 if some event such as a signal ended the wait. | ||
121 | */ | ||
122 | |||
123 | static void vt_event_wait(struct vt_event_wait *vw) | ||
124 | { | ||
125 | unsigned long flags; | ||
126 | /* Prepare the event */ | ||
127 | INIT_LIST_HEAD(&vw->list); | ||
128 | vw->done = 0; | ||
129 | /* Queue our event */ | ||
130 | spin_lock_irqsave(&vt_event_lock, flags); | ||
131 | list_add(&vw->list, &vt_events); | ||
132 | spin_unlock_irqrestore(&vt_event_lock, flags); | ||
133 | /* Wait for it to pass */ | ||
134 | wait_event_interruptible(vt_event_waitqueue, vw->done); | ||
135 | /* Dequeue it */ | ||
136 | spin_lock_irqsave(&vt_event_lock, flags); | ||
137 | list_del(&vw->list); | ||
138 | spin_unlock_irqrestore(&vt_event_lock, flags); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * vt_event_wait_ioctl - event ioctl handler | ||
143 | * @arg: argument to ioctl | ||
144 | * | ||
145 | * Implement the VT_WAITEVENT ioctl using the VT event interface | ||
146 | */ | ||
147 | |||
148 | static int vt_event_wait_ioctl(struct vt_event __user *event) | ||
149 | { | ||
150 | struct vt_event_wait vw; | ||
151 | |||
152 | if (copy_from_user(&vw.event, event, sizeof(struct vt_event))) | ||
153 | return -EFAULT; | ||
154 | /* Highest supported event for now */ | ||
155 | if (vw.event.event & ~VT_MAX_EVENT) | ||
156 | return -EINVAL; | ||
157 | |||
158 | vt_event_wait(&vw); | ||
159 | /* If it occurred report it */ | ||
160 | if (vw.done) { | ||
161 | if (copy_to_user(event, &vw.event, sizeof(struct vt_event))) | ||
162 | return -EFAULT; | ||
163 | return 0; | ||
164 | } | ||
165 | return -EINTR; | ||
166 | } | ||
167 | |||
168 | /** | ||
169 | * vt_waitactive - active console wait | ||
170 | * @event: event code | ||
171 | * @n: new console | ||
172 | * | ||
173 | * Helper for event waits. Used to implement the legacy | ||
174 | * event waiting ioctls in terms of events | ||
175 | */ | ||
176 | |||
177 | int vt_waitactive(int n) | ||
178 | { | ||
179 | struct vt_event_wait vw; | ||
180 | do { | ||
181 | if (n == fg_console + 1) | ||
182 | break; | ||
183 | vw.event.event = VT_EVENT_SWITCH; | ||
184 | vt_event_wait(&vw); | ||
185 | if (vw.done == 0) | ||
186 | return -EINTR; | ||
187 | } while (vw.event.new != n); | ||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | /* | ||
65 | * these are the valid i/o ports we're allowed to change. they map all the | 192 | * these are the valid i/o ports we're allowed to change. they map all the |
66 | * video ports | 193 | * video ports |
67 | */ | 194 | */ |
@@ -360,6 +487,8 @@ do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_ | |||
360 | return 0; | 487 | return 0; |
361 | } | 488 | } |
362 | 489 | ||
490 | |||
491 | |||
363 | /* | 492 | /* |
364 | * We handle the console-specific ioctl's here. We allow the | 493 | * We handle the console-specific ioctl's here. We allow the |
365 | * capability to modify any console, not just the fg_console. | 494 | * capability to modify any console, not just the fg_console. |
@@ -851,7 +980,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, | |||
851 | if (arg == 0 || arg > MAX_NR_CONSOLES) | 980 | if (arg == 0 || arg > MAX_NR_CONSOLES) |
852 | ret = -ENXIO; | 981 | ret = -ENXIO; |
853 | else | 982 | else |
854 | ret = vt_waitactive(arg - 1); | 983 | ret = vt_waitactive(arg); |
855 | break; | 984 | break; |
856 | 985 | ||
857 | /* | 986 | /* |
@@ -1159,6 +1288,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, | |||
1159 | ret = put_user(vc->vc_hi_font_mask, | 1288 | ret = put_user(vc->vc_hi_font_mask, |
1160 | (unsigned short __user *)arg); | 1289 | (unsigned short __user *)arg); |
1161 | break; | 1290 | break; |
1291 | case VT_WAITEVENT: | ||
1292 | ret = vt_event_wait_ioctl((struct vt_event __user *)arg); | ||
1293 | break; | ||
1162 | default: | 1294 | default: |
1163 | ret = -ENOIOCTLCMD; | 1295 | ret = -ENOIOCTLCMD; |
1164 | } | 1296 | } |
@@ -1170,54 +1302,6 @@ eperm: | |||
1170 | goto out; | 1302 | goto out; |
1171 | } | 1303 | } |
1172 | 1304 | ||
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) | 1305 | void reset_vc(struct vc_data *vc) |
1222 | { | 1306 | { |
1223 | vc->vc_mode = KD_TEXT; | 1307 | vc->vc_mode = KD_TEXT; |
@@ -1262,6 +1346,7 @@ void vc_SAK(struct work_struct *work) | |||
1262 | static void complete_change_console(struct vc_data *vc) | 1346 | static void complete_change_console(struct vc_data *vc) |
1263 | { | 1347 | { |
1264 | unsigned char old_vc_mode; | 1348 | unsigned char old_vc_mode; |
1349 | int old = fg_console; | ||
1265 | 1350 | ||
1266 | last_console = fg_console; | 1351 | last_console = fg_console; |
1267 | 1352 | ||
@@ -1325,7 +1410,7 @@ static void complete_change_console(struct vc_data *vc) | |||
1325 | /* | 1410 | /* |
1326 | * Wake anyone waiting for their VT to activate | 1411 | * Wake anyone waiting for their VT to activate |
1327 | */ | 1412 | */ |
1328 | vt_wake_waitactive(); | 1413 | vt_event_post(VT_EVENT_SWITCH, old, vc->vc_num); |
1329 | return; | 1414 | return; |
1330 | } | 1415 | } |
1331 | 1416 | ||