1*2067fd92SSamuel Thibault // SPDX-License-Identifier: GPL-2.0 2*2067fd92SSamuel Thibault #include <linux/slab.h> /* for kmalloc */ 3*2067fd92SSamuel Thibault #include <linux/consolemap.h> 4*2067fd92SSamuel Thibault #include <linux/interrupt.h> 5*2067fd92SSamuel Thibault #include <linux/sched.h> 6*2067fd92SSamuel Thibault #include <linux/device.h> /* for dev_warn */ 7*2067fd92SSamuel Thibault #include <linux/selection.h> 8*2067fd92SSamuel Thibault #include <linux/workqueue.h> 9*2067fd92SSamuel Thibault #include <linux/tty.h> 10*2067fd92SSamuel Thibault #include <linux/tty_flip.h> 11*2067fd92SSamuel Thibault #include <linux/atomic.h> 12*2067fd92SSamuel Thibault #include <linux/console.h> 13*2067fd92SSamuel Thibault 14*2067fd92SSamuel Thibault #include "speakup.h" 15*2067fd92SSamuel Thibault 16*2067fd92SSamuel Thibault unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ 17*2067fd92SSamuel Thibault struct vc_data *spk_sel_cons; 18*2067fd92SSamuel Thibault 19*2067fd92SSamuel Thibault struct speakup_selection_work { 20*2067fd92SSamuel Thibault struct work_struct work; 21*2067fd92SSamuel Thibault struct tiocl_selection sel; 22*2067fd92SSamuel Thibault struct tty_struct *tty; 23*2067fd92SSamuel Thibault }; 24*2067fd92SSamuel Thibault 25*2067fd92SSamuel Thibault void speakup_clear_selection(void) 26*2067fd92SSamuel Thibault { 27*2067fd92SSamuel Thibault console_lock(); 28*2067fd92SSamuel Thibault clear_selection(); 29*2067fd92SSamuel Thibault console_unlock(); 30*2067fd92SSamuel Thibault } 31*2067fd92SSamuel Thibault 32*2067fd92SSamuel Thibault static void __speakup_set_selection(struct work_struct *work) 33*2067fd92SSamuel Thibault { 34*2067fd92SSamuel Thibault struct speakup_selection_work *ssw = 35*2067fd92SSamuel Thibault container_of(work, struct speakup_selection_work, work); 36*2067fd92SSamuel Thibault 37*2067fd92SSamuel Thibault struct tty_struct *tty; 38*2067fd92SSamuel Thibault struct tiocl_selection sel; 39*2067fd92SSamuel Thibault 40*2067fd92SSamuel Thibault sel = ssw->sel; 41*2067fd92SSamuel Thibault 42*2067fd92SSamuel Thibault /* this ensures we copy sel before releasing the lock below */ 43*2067fd92SSamuel Thibault rmb(); 44*2067fd92SSamuel Thibault 45*2067fd92SSamuel Thibault /* release the lock by setting tty of the struct to NULL */ 46*2067fd92SSamuel Thibault tty = xchg(&ssw->tty, NULL); 47*2067fd92SSamuel Thibault 48*2067fd92SSamuel Thibault if (spk_sel_cons != vc_cons[fg_console].d) { 49*2067fd92SSamuel Thibault spk_sel_cons = vc_cons[fg_console].d; 50*2067fd92SSamuel Thibault pr_warn("Selection: mark console not the same as cut\n"); 51*2067fd92SSamuel Thibault goto unref; 52*2067fd92SSamuel Thibault } 53*2067fd92SSamuel Thibault 54*2067fd92SSamuel Thibault set_selection_kernel(&sel, tty); 55*2067fd92SSamuel Thibault 56*2067fd92SSamuel Thibault unref: 57*2067fd92SSamuel Thibault tty_kref_put(tty); 58*2067fd92SSamuel Thibault } 59*2067fd92SSamuel Thibault 60*2067fd92SSamuel Thibault static struct speakup_selection_work speakup_sel_work = { 61*2067fd92SSamuel Thibault .work = __WORK_INITIALIZER(speakup_sel_work.work, 62*2067fd92SSamuel Thibault __speakup_set_selection) 63*2067fd92SSamuel Thibault }; 64*2067fd92SSamuel Thibault 65*2067fd92SSamuel Thibault int speakup_set_selection(struct tty_struct *tty) 66*2067fd92SSamuel Thibault { 67*2067fd92SSamuel Thibault /* we get kref here first in order to avoid a subtle race when 68*2067fd92SSamuel Thibault * cancelling selection work. getting kref first establishes the 69*2067fd92SSamuel Thibault * invariant that if speakup_sel_work.tty is not NULL when 70*2067fd92SSamuel Thibault * speakup_cancel_selection() is called, it must be the case that a put 71*2067fd92SSamuel Thibault * kref is pending. 72*2067fd92SSamuel Thibault */ 73*2067fd92SSamuel Thibault tty_kref_get(tty); 74*2067fd92SSamuel Thibault if (cmpxchg(&speakup_sel_work.tty, NULL, tty)) { 75*2067fd92SSamuel Thibault tty_kref_put(tty); 76*2067fd92SSamuel Thibault return -EBUSY; 77*2067fd92SSamuel Thibault } 78*2067fd92SSamuel Thibault /* now we have the 'lock' by setting tty member of 79*2067fd92SSamuel Thibault * speakup_selection_work. wmb() ensures that writes to 80*2067fd92SSamuel Thibault * speakup_sel_work don't happen before cmpxchg() above. 81*2067fd92SSamuel Thibault */ 82*2067fd92SSamuel Thibault wmb(); 83*2067fd92SSamuel Thibault 84*2067fd92SSamuel Thibault speakup_sel_work.sel.xs = spk_xs + 1; 85*2067fd92SSamuel Thibault speakup_sel_work.sel.ys = spk_ys + 1; 86*2067fd92SSamuel Thibault speakup_sel_work.sel.xe = spk_xe + 1; 87*2067fd92SSamuel Thibault speakup_sel_work.sel.ye = spk_ye + 1; 88*2067fd92SSamuel Thibault speakup_sel_work.sel.sel_mode = TIOCL_SELCHAR; 89*2067fd92SSamuel Thibault 90*2067fd92SSamuel Thibault schedule_work_on(WORK_CPU_UNBOUND, &speakup_sel_work.work); 91*2067fd92SSamuel Thibault 92*2067fd92SSamuel Thibault return 0; 93*2067fd92SSamuel Thibault } 94*2067fd92SSamuel Thibault 95*2067fd92SSamuel Thibault void speakup_cancel_selection(void) 96*2067fd92SSamuel Thibault { 97*2067fd92SSamuel Thibault struct tty_struct *tty; 98*2067fd92SSamuel Thibault 99*2067fd92SSamuel Thibault cancel_work_sync(&speakup_sel_work.work); 100*2067fd92SSamuel Thibault /* setting to null so that if work fails to run and we cancel it, 101*2067fd92SSamuel Thibault * we can run it again without getting EBUSY forever from there on. 102*2067fd92SSamuel Thibault * we need to use xchg here to avoid race with speakup_set_selection() 103*2067fd92SSamuel Thibault */ 104*2067fd92SSamuel Thibault tty = xchg(&speakup_sel_work.tty, NULL); 105*2067fd92SSamuel Thibault if (tty) 106*2067fd92SSamuel Thibault tty_kref_put(tty); 107*2067fd92SSamuel Thibault } 108*2067fd92SSamuel Thibault 109*2067fd92SSamuel Thibault static void __speakup_paste_selection(struct work_struct *work) 110*2067fd92SSamuel Thibault { 111*2067fd92SSamuel Thibault struct speakup_selection_work *ssw = 112*2067fd92SSamuel Thibault container_of(work, struct speakup_selection_work, work); 113*2067fd92SSamuel Thibault struct tty_struct *tty = xchg(&ssw->tty, NULL); 114*2067fd92SSamuel Thibault 115*2067fd92SSamuel Thibault paste_selection(tty); 116*2067fd92SSamuel Thibault tty_kref_put(tty); 117*2067fd92SSamuel Thibault } 118*2067fd92SSamuel Thibault 119*2067fd92SSamuel Thibault static struct speakup_selection_work speakup_paste_work = { 120*2067fd92SSamuel Thibault .work = __WORK_INITIALIZER(speakup_paste_work.work, 121*2067fd92SSamuel Thibault __speakup_paste_selection) 122*2067fd92SSamuel Thibault }; 123*2067fd92SSamuel Thibault 124*2067fd92SSamuel Thibault int speakup_paste_selection(struct tty_struct *tty) 125*2067fd92SSamuel Thibault { 126*2067fd92SSamuel Thibault tty_kref_get(tty); 127*2067fd92SSamuel Thibault if (cmpxchg(&speakup_paste_work.tty, NULL, tty)) { 128*2067fd92SSamuel Thibault tty_kref_put(tty); 129*2067fd92SSamuel Thibault return -EBUSY; 130*2067fd92SSamuel Thibault } 131*2067fd92SSamuel Thibault 132*2067fd92SSamuel Thibault schedule_work_on(WORK_CPU_UNBOUND, &speakup_paste_work.work); 133*2067fd92SSamuel Thibault return 0; 134*2067fd92SSamuel Thibault } 135*2067fd92SSamuel Thibault 136*2067fd92SSamuel Thibault void speakup_cancel_paste(void) 137*2067fd92SSamuel Thibault { 138*2067fd92SSamuel Thibault struct tty_struct *tty; 139*2067fd92SSamuel Thibault 140*2067fd92SSamuel Thibault cancel_work_sync(&speakup_paste_work.work); 141*2067fd92SSamuel Thibault tty = xchg(&speakup_paste_work.tty, NULL); 142*2067fd92SSamuel Thibault if (tty) 143*2067fd92SSamuel Thibault tty_kref_put(tty); 144*2067fd92SSamuel Thibault } 145