1 |
62 |
marcus.erl |
/*
|
2 |
|
|
* IBM eServer Hypervisor Virtual Console Server Device Driver
|
3 |
|
|
* Copyright (C) 2003, 2004 IBM Corp.
|
4 |
|
|
* Ryan S. Arnold (rsa@us.ibm.com)
|
5 |
|
|
*
|
6 |
|
|
* This program is free software; you can redistribute it and/or modify
|
7 |
|
|
* it under the terms of the GNU General Public License as published by
|
8 |
|
|
* the Free Software Foundation; either version 2 of the License, or
|
9 |
|
|
* (at your option) any later version.
|
10 |
|
|
*
|
11 |
|
|
* This program is distributed in the hope that it will be useful,
|
12 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14 |
|
|
* GNU General Public License for more details.
|
15 |
|
|
*
|
16 |
|
|
* You should have received a copy of the GNU General Public License
|
17 |
|
|
* along with this program; if not, write to the Free Software
|
18 |
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19 |
|
|
*
|
20 |
|
|
* Author(s) : Ryan S. Arnold <rsa@us.ibm.com>
|
21 |
|
|
*
|
22 |
|
|
* This is the device driver for the IBM Hypervisor Virtual Console Server,
|
23 |
|
|
* "hvcs". The IBM hvcs provides a tty driver interface to allow Linux
|
24 |
|
|
* user space applications access to the system consoles of logically
|
25 |
|
|
* partitioned operating systems, e.g. Linux, running on the same partitioned
|
26 |
|
|
* Power5 ppc64 system. Physical hardware consoles per partition are not
|
27 |
|
|
* practical on this hardware so system consoles are accessed by this driver
|
28 |
|
|
* using inter-partition firmware interfaces to virtual terminal devices.
|
29 |
|
|
*
|
30 |
|
|
* A vty is known to the HMC as a "virtual serial server adapter". It is a
|
31 |
|
|
* virtual terminal device that is created by firmware upon partition creation
|
32 |
|
|
* to act as a partitioned OS's console device.
|
33 |
|
|
*
|
34 |
|
|
* Firmware dynamically (via hotplug) exposes vty-servers to a running ppc64
|
35 |
|
|
* Linux system upon their creation by the HMC or their exposure during boot.
|
36 |
|
|
* The non-user interactive backend of this driver is implemented as a vio
|
37 |
|
|
* device driver so that it can receive notification of vty-server lifetimes
|
38 |
|
|
* after it registers with the vio bus to handle vty-server probe and remove
|
39 |
|
|
* callbacks.
|
40 |
|
|
*
|
41 |
|
|
* Many vty-servers can be configured to connect to one vty, but a vty can
|
42 |
|
|
* only be actively connected to by a single vty-server, in any manner, at one
|
43 |
|
|
* time. If the HMC is currently hosting the console for a target Linux
|
44 |
|
|
* partition; attempts to open the tty device to the partition's console using
|
45 |
|
|
* the hvcs on any partition will return -EBUSY with every open attempt until
|
46 |
|
|
* the HMC frees the connection between its vty-server and the desired
|
47 |
|
|
* partition's vty device. Conversely, a vty-server may only be connected to
|
48 |
|
|
* a single vty at one time even though it may have several configured vty
|
49 |
|
|
* partner possibilities.
|
50 |
|
|
*
|
51 |
|
|
* Firmware does not provide notification of vty partner changes to this
|
52 |
|
|
* driver. This means that an HMC Super Admin may add or remove partner vtys
|
53 |
|
|
* from a vty-server's partner list but the changes will not be signaled to
|
54 |
|
|
* the vty-server. Firmware only notifies the driver when a vty-server is
|
55 |
|
|
* added or removed from the system. To compensate for this deficiency, this
|
56 |
|
|
* driver implements a sysfs update attribute which provides a method for
|
57 |
|
|
* rescanning partner information upon a user's request.
|
58 |
|
|
*
|
59 |
|
|
* Each vty-server, prior to being exposed to this driver is reference counted
|
60 |
|
|
* using the 2.6 Linux kernel kobject construct. This kobject is also used by
|
61 |
|
|
* the vio bus to provide a vio device sysfs entry that this driver attaches
|
62 |
|
|
* device specific attributes to, including partner information. The vio bus
|
63 |
|
|
* framework also provides a sysfs entry for each vio driver. The hvcs driver
|
64 |
|
|
* provides driver attributes in this entry.
|
65 |
|
|
*
|
66 |
|
|
* For direction on installation and usage of this driver please reference
|
67 |
|
|
* Documentation/powerpc/hvcs.txt.
|
68 |
|
|
*/
|
69 |
|
|
|
70 |
|
|
#include <linux/device.h>
|
71 |
|
|
#include <linux/init.h>
|
72 |
|
|
#include <linux/interrupt.h>
|
73 |
|
|
#include <linux/kernel.h>
|
74 |
|
|
#include <linux/kobject.h>
|
75 |
|
|
#include <linux/kthread.h>
|
76 |
|
|
#include <linux/list.h>
|
77 |
|
|
#include <linux/major.h>
|
78 |
|
|
#include <linux/module.h>
|
79 |
|
|
#include <linux/moduleparam.h>
|
80 |
|
|
#include <linux/sched.h>
|
81 |
|
|
#include <linux/spinlock.h>
|
82 |
|
|
#include <linux/stat.h>
|
83 |
|
|
#include <linux/tty.h>
|
84 |
|
|
#include <linux/tty_flip.h>
|
85 |
|
|
#include <asm/hvconsole.h>
|
86 |
|
|
#include <asm/hvcserver.h>
|
87 |
|
|
#include <asm/uaccess.h>
|
88 |
|
|
#include <asm/vio.h>
|
89 |
|
|
|
90 |
|
|
/*
|
91 |
|
|
* 1.3.0 -> 1.3.1 In hvcs_open memset(..,0x00,..) instead of memset(..,0x3F,00).
|
92 |
|
|
* Removed braces around single statements following conditionals. Removed '=
|
93 |
|
|
* 0' after static int declarations since these default to zero. Removed
|
94 |
|
|
* list_for_each_safe() and replaced with list_for_each_entry() in
|
95 |
|
|
* hvcs_get_by_index(). The 'safe' version is un-needed now that the driver is
|
96 |
|
|
* using spinlocks. Changed spin_lock_irqsave() to spin_lock() when locking
|
97 |
|
|
* hvcs_structs_lock and hvcs_pi_lock since these are not touched in an int
|
98 |
|
|
* handler. Initialized hvcs_structs_lock and hvcs_pi_lock to
|
99 |
|
|
* SPIN_LOCK_UNLOCKED at declaration time rather than in hvcs_module_init().
|
100 |
|
|
* Added spin_lock around list_del() in destroy_hvcs_struct() to protect the
|
101 |
|
|
* list traversals from a deletion. Removed '= NULL' from pointer declaration
|
102 |
|
|
* statements since they are initialized NULL by default. Removed wmb()
|
103 |
|
|
* instances from hvcs_try_write(). They probably aren't needed with locking in
|
104 |
|
|
* place. Added check and cleanup for hvcs_pi_buff = kmalloc() in
|
105 |
|
|
* hvcs_module_init(). Exposed hvcs_struct.index via a sysfs attribute so that
|
106 |
|
|
* the coupling between /dev/hvcs* and a vty-server can be automatically
|
107 |
|
|
* determined. Moved kobject_put() in hvcs_open outside of the
|
108 |
|
|
* spin_unlock_irqrestore().
|
109 |
|
|
*
|
110 |
|
|
* 1.3.1 -> 1.3.2 Changed method for determining hvcs_struct->index and had it
|
111 |
|
|
* align with how the tty layer always assigns the lowest index available. This
|
112 |
|
|
* change resulted in a list of ints that denotes which indexes are available.
|
113 |
|
|
* Device additions and removals use the new hvcs_get_index() and
|
114 |
|
|
* hvcs_return_index() helper functions. The list is created with
|
115 |
|
|
* hvsc_alloc_index_list() and it is destroyed with hvcs_free_index_list().
|
116 |
|
|
* Without these fixes hotplug vty-server adapter support goes crazy with this
|
117 |
|
|
* driver if the user removes a vty-server adapter. Moved free_irq() outside of
|
118 |
|
|
* the hvcs_final_close() function in order to get it out of the spinlock.
|
119 |
|
|
* Rearranged hvcs_close(). Cleaned up some printks and did some housekeeping
|
120 |
|
|
* on the changelog. Removed local CLC_LENGTH and used HVCS_CLC_LENGTH from
|
121 |
|
|
* include/asm-powerpc/hvcserver.h
|
122 |
|
|
*
|
123 |
|
|
* 1.3.2 -> 1.3.3 Replaced yield() in hvcs_close() with tty_wait_until_sent() to
|
124 |
|
|
* prevent possible lockup with realtime scheduling as similarily pointed out by
|
125 |
|
|
* akpm in hvc_console. Changed resulted in the removal of hvcs_final_close()
|
126 |
|
|
* to reorder cleanup operations and prevent discarding of pending data during
|
127 |
|
|
* an hvcs_close(). Removed spinlock protection of hvcs_struct data members in
|
128 |
|
|
* hvcs_write_room() and hvcs_chars_in_buffer() because they aren't needed.
|
129 |
|
|
*/
|
130 |
|
|
|
131 |
|
|
#define HVCS_DRIVER_VERSION "1.3.3"
|
132 |
|
|
|
133 |
|
|
MODULE_AUTHOR("Ryan S. Arnold <rsa@us.ibm.com>");
|
134 |
|
|
MODULE_DESCRIPTION("IBM hvcs (Hypervisor Virtual Console Server) Driver");
|
135 |
|
|
MODULE_LICENSE("GPL");
|
136 |
|
|
MODULE_VERSION(HVCS_DRIVER_VERSION);
|
137 |
|
|
|
138 |
|
|
/*
|
139 |
|
|
* Wait this long per iteration while trying to push buffered data to the
|
140 |
|
|
* hypervisor before allowing the tty to complete a close operation.
|
141 |
|
|
*/
|
142 |
|
|
#define HVCS_CLOSE_WAIT (HZ/100) /* 1/10 of a second */
|
143 |
|
|
|
144 |
|
|
/*
|
145 |
|
|
* Since the Linux TTY code does not currently (2-04-2004) support dynamic
|
146 |
|
|
* addition of tty derived devices and we shouldn't allocate thousands of
|
147 |
|
|
* tty_device pointers when the number of vty-server & vty partner connections
|
148 |
|
|
* will most often be much lower than this, we'll arbitrarily allocate
|
149 |
|
|
* HVCS_DEFAULT_SERVER_ADAPTERS tty_structs and cdev's by default when we
|
150 |
|
|
* register the tty_driver. This can be overridden using an insmod parameter.
|
151 |
|
|
*/
|
152 |
|
|
#define HVCS_DEFAULT_SERVER_ADAPTERS 64
|
153 |
|
|
|
154 |
|
|
/*
|
155 |
|
|
* The user can't insmod with more than HVCS_MAX_SERVER_ADAPTERS hvcs device
|
156 |
|
|
* nodes as a sanity check. Theoretically there can be over 1 Billion
|
157 |
|
|
* vty-server & vty partner connections.
|
158 |
|
|
*/
|
159 |
|
|
#define HVCS_MAX_SERVER_ADAPTERS 1024
|
160 |
|
|
|
161 |
|
|
/*
|
162 |
|
|
* We let Linux assign us a major number and we start the minors at zero. There
|
163 |
|
|
* is no intuitive mapping between minor number and the target vty-server
|
164 |
|
|
* adapter except that each new vty-server adapter is always assigned to the
|
165 |
|
|
* smallest minor number available.
|
166 |
|
|
*/
|
167 |
|
|
#define HVCS_MINOR_START 0
|
168 |
|
|
|
169 |
|
|
/*
|
170 |
|
|
* The hcall interface involves putting 8 chars into each of two registers.
|
171 |
|
|
* We load up those 2 registers (in arch/powerpc/platforms/pseries/hvconsole.c)
|
172 |
|
|
* by casting char[16] to long[2]. It would work without __ALIGNED__, but a
|
173 |
|
|
* little (tiny) bit slower because an unaligned load is slower than aligned
|
174 |
|
|
* load.
|
175 |
|
|
*/
|
176 |
|
|
#define __ALIGNED__ __attribute__((__aligned__(8)))
|
177 |
|
|
|
178 |
|
|
/*
|
179 |
|
|
* How much data can firmware send with each hvc_put_chars()? Maybe this
|
180 |
|
|
* should be moved into an architecture specific area.
|
181 |
|
|
*/
|
182 |
|
|
#define HVCS_BUFF_LEN 16
|
183 |
|
|
|
184 |
|
|
/*
|
185 |
|
|
* This is the maximum amount of data we'll let the user send us (hvcs_write) at
|
186 |
|
|
* once in a chunk as a sanity check.
|
187 |
|
|
*/
|
188 |
|
|
#define HVCS_MAX_FROM_USER 4096
|
189 |
|
|
|
190 |
|
|
/*
|
191 |
|
|
* Be careful when adding flags to this line discipline. Don't add anything
|
192 |
|
|
* that will cause echoing or we'll go into recursive loop echoing chars back
|
193 |
|
|
* and forth with the console drivers.
|
194 |
|
|
*/
|
195 |
|
|
static struct ktermios hvcs_tty_termios = {
|
196 |
|
|
.c_iflag = IGNBRK | IGNPAR,
|
197 |
|
|
.c_oflag = OPOST,
|
198 |
|
|
.c_cflag = B38400 | CS8 | CREAD | HUPCL,
|
199 |
|
|
.c_cc = INIT_C_CC,
|
200 |
|
|
.c_ispeed = 38400,
|
201 |
|
|
.c_ospeed = 38400
|
202 |
|
|
};
|
203 |
|
|
|
204 |
|
|
/*
|
205 |
|
|
* This value is used to take the place of a command line parameter when the
|
206 |
|
|
* module is inserted. It starts as -1 and stays as such if the user doesn't
|
207 |
|
|
* specify a module insmod parameter. If they DO specify one then it is set to
|
208 |
|
|
* the value of the integer passed in.
|
209 |
|
|
*/
|
210 |
|
|
static int hvcs_parm_num_devs = -1;
|
211 |
|
|
module_param(hvcs_parm_num_devs, int, 0);
|
212 |
|
|
|
213 |
|
|
static const char hvcs_driver_name[] = "hvcs";
|
214 |
|
|
static const char hvcs_device_node[] = "hvcs";
|
215 |
|
|
static const char hvcs_driver_string[]
|
216 |
|
|
= "IBM hvcs (Hypervisor Virtual Console Server) Driver";
|
217 |
|
|
|
218 |
|
|
/* Status of partner info rescan triggered via sysfs. */
|
219 |
|
|
static int hvcs_rescan_status;
|
220 |
|
|
|
221 |
|
|
static struct tty_driver *hvcs_tty_driver;
|
222 |
|
|
|
223 |
|
|
/*
|
224 |
|
|
* In order to be somewhat sane this driver always associates the hvcs_struct
|
225 |
|
|
* index element with the numerically equal tty->index. This means that a
|
226 |
|
|
* hotplugged vty-server adapter will always map to the lowest index valued
|
227 |
|
|
* device node. If vty-servers were hotplug removed from the system and then
|
228 |
|
|
* new ones added the new vty-server may have the largest slot number of all
|
229 |
|
|
* the vty-server adapters in the partition but it may have the lowest dev node
|
230 |
|
|
* index of all the adapters due to the hole left by the hotplug removed
|
231 |
|
|
* adapter. There are a set of functions provided to get the lowest index for
|
232 |
|
|
* a new device as well as return the index to the list. This list is allocated
|
233 |
|
|
* with a number of elements equal to the number of device nodes requested when
|
234 |
|
|
* the module was inserted.
|
235 |
|
|
*/
|
236 |
|
|
static int *hvcs_index_list;
|
237 |
|
|
|
238 |
|
|
/*
|
239 |
|
|
* How large is the list? This is kept for traversal since the list is
|
240 |
|
|
* dynamically created.
|
241 |
|
|
*/
|
242 |
|
|
static int hvcs_index_count;
|
243 |
|
|
|
244 |
|
|
/*
|
245 |
|
|
* Used by the khvcsd to pick up I/O operations when the kernel_thread is
|
246 |
|
|
* already awake but potentially shifted to TASK_INTERRUPTIBLE state.
|
247 |
|
|
*/
|
248 |
|
|
static int hvcs_kicked;
|
249 |
|
|
|
250 |
|
|
/*
|
251 |
|
|
* Use by the kthread construct for task operations like waking the sleeping
|
252 |
|
|
* thread and stopping the kthread.
|
253 |
|
|
*/
|
254 |
|
|
static struct task_struct *hvcs_task;
|
255 |
|
|
|
256 |
|
|
/*
|
257 |
|
|
* We allocate this for the use of all of the hvcs_structs when they fetch
|
258 |
|
|
* partner info.
|
259 |
|
|
*/
|
260 |
|
|
static unsigned long *hvcs_pi_buff;
|
261 |
|
|
|
262 |
|
|
/* Only allow one hvcs_struct to use the hvcs_pi_buff at a time. */
|
263 |
|
|
static DEFINE_SPINLOCK(hvcs_pi_lock);
|
264 |
|
|
|
265 |
|
|
/* One vty-server per hvcs_struct */
|
266 |
|
|
struct hvcs_struct {
|
267 |
|
|
spinlock_t lock;
|
268 |
|
|
|
269 |
|
|
/*
|
270 |
|
|
* This index identifies this hvcs device as the complement to a
|
271 |
|
|
* specific tty index.
|
272 |
|
|
*/
|
273 |
|
|
unsigned int index;
|
274 |
|
|
|
275 |
|
|
struct tty_struct *tty;
|
276 |
|
|
unsigned int open_count;
|
277 |
|
|
|
278 |
|
|
/*
|
279 |
|
|
* Used to tell the driver kernel_thread what operations need to take
|
280 |
|
|
* place upon this hvcs_struct instance.
|
281 |
|
|
*/
|
282 |
|
|
int todo_mask;
|
283 |
|
|
|
284 |
|
|
/*
|
285 |
|
|
* This buffer is required so that when hvcs_write_room() reports that
|
286 |
|
|
* it can send HVCS_BUFF_LEN characters that it will buffer the full
|
287 |
|
|
* HVCS_BUFF_LEN characters if need be. This is essential for opost
|
288 |
|
|
* writes since they do not do high level buffering and expect to be
|
289 |
|
|
* able to send what the driver commits to sending buffering
|
290 |
|
|
* [e.g. tab to space conversions in n_tty.c opost()].
|
291 |
|
|
*/
|
292 |
|
|
char buffer[HVCS_BUFF_LEN];
|
293 |
|
|
int chars_in_buffer;
|
294 |
|
|
|
295 |
|
|
/*
|
296 |
|
|
* Any variable below the kobject is valid before a tty is connected and
|
297 |
|
|
* stays valid after the tty is disconnected. These shouldn't be
|
298 |
|
|
* whacked until the koject refcount reaches zero though some entries
|
299 |
|
|
* may be changed via sysfs initiatives.
|
300 |
|
|
*/
|
301 |
|
|
struct kobject kobj; /* ref count & hvcs_struct lifetime */
|
302 |
|
|
int connected; /* is the vty-server currently connected to a vty? */
|
303 |
|
|
uint32_t p_unit_address; /* partner unit address */
|
304 |
|
|
uint32_t p_partition_ID; /* partner partition ID */
|
305 |
|
|
char p_location_code[HVCS_CLC_LENGTH + 1]; /* CLC + Null Term */
|
306 |
|
|
struct list_head next; /* list management */
|
307 |
|
|
struct vio_dev *vdev;
|
308 |
|
|
};
|
309 |
|
|
|
310 |
|
|
/* Required to back map a kobject to its containing object */
|
311 |
|
|
#define from_kobj(kobj) container_of(kobj, struct hvcs_struct, kobj)
|
312 |
|
|
|
313 |
|
|
static struct list_head hvcs_structs = LIST_HEAD_INIT(hvcs_structs);
|
314 |
|
|
static DEFINE_SPINLOCK(hvcs_structs_lock);
|
315 |
|
|
|
316 |
|
|
static void hvcs_unthrottle(struct tty_struct *tty);
|
317 |
|
|
static void hvcs_throttle(struct tty_struct *tty);
|
318 |
|
|
static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance);
|
319 |
|
|
|
320 |
|
|
static int hvcs_write(struct tty_struct *tty,
|
321 |
|
|
const unsigned char *buf, int count);
|
322 |
|
|
static int hvcs_write_room(struct tty_struct *tty);
|
323 |
|
|
static int hvcs_chars_in_buffer(struct tty_struct *tty);
|
324 |
|
|
|
325 |
|
|
static int hvcs_has_pi(struct hvcs_struct *hvcsd);
|
326 |
|
|
static void hvcs_set_pi(struct hvcs_partner_info *pi,
|
327 |
|
|
struct hvcs_struct *hvcsd);
|
328 |
|
|
static int hvcs_get_pi(struct hvcs_struct *hvcsd);
|
329 |
|
|
static int hvcs_rescan_devices_list(void);
|
330 |
|
|
|
331 |
|
|
static int hvcs_partner_connect(struct hvcs_struct *hvcsd);
|
332 |
|
|
static void hvcs_partner_free(struct hvcs_struct *hvcsd);
|
333 |
|
|
|
334 |
|
|
static int hvcs_enable_device(struct hvcs_struct *hvcsd,
|
335 |
|
|
uint32_t unit_address, unsigned int irq, struct vio_dev *dev);
|
336 |
|
|
|
337 |
|
|
static void destroy_hvcs_struct(struct kobject *kobj);
|
338 |
|
|
static int hvcs_open(struct tty_struct *tty, struct file *filp);
|
339 |
|
|
static void hvcs_close(struct tty_struct *tty, struct file *filp);
|
340 |
|
|
static void hvcs_hangup(struct tty_struct * tty);
|
341 |
|
|
|
342 |
|
|
static int __devinit hvcs_probe(struct vio_dev *dev,
|
343 |
|
|
const struct vio_device_id *id);
|
344 |
|
|
static int __devexit hvcs_remove(struct vio_dev *dev);
|
345 |
|
|
static int __init hvcs_module_init(void);
|
346 |
|
|
static void __exit hvcs_module_exit(void);
|
347 |
|
|
|
348 |
|
|
#define HVCS_SCHED_READ 0x00000001
|
349 |
|
|
#define HVCS_QUICK_READ 0x00000002
|
350 |
|
|
#define HVCS_TRY_WRITE 0x00000004
|
351 |
|
|
#define HVCS_READ_MASK (HVCS_SCHED_READ | HVCS_QUICK_READ)
|
352 |
|
|
|
353 |
|
|
static inline struct hvcs_struct *from_vio_dev(struct vio_dev *viod)
|
354 |
|
|
{
|
355 |
|
|
return viod->dev.driver_data;
|
356 |
|
|
}
|
357 |
|
|
/* The sysfs interface for the driver and devices */
|
358 |
|
|
|
359 |
|
|
static ssize_t hvcs_partner_vtys_show(struct device *dev, struct device_attribute *attr, char *buf)
|
360 |
|
|
{
|
361 |
|
|
struct vio_dev *viod = to_vio_dev(dev);
|
362 |
|
|
struct hvcs_struct *hvcsd = from_vio_dev(viod);
|
363 |
|
|
unsigned long flags;
|
364 |
|
|
int retval;
|
365 |
|
|
|
366 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
367 |
|
|
retval = sprintf(buf, "%X\n", hvcsd->p_unit_address);
|
368 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
369 |
|
|
return retval;
|
370 |
|
|
}
|
371 |
|
|
static DEVICE_ATTR(partner_vtys, S_IRUGO, hvcs_partner_vtys_show, NULL);
|
372 |
|
|
|
373 |
|
|
static ssize_t hvcs_partner_clcs_show(struct device *dev, struct device_attribute *attr, char *buf)
|
374 |
|
|
{
|
375 |
|
|
struct vio_dev *viod = to_vio_dev(dev);
|
376 |
|
|
struct hvcs_struct *hvcsd = from_vio_dev(viod);
|
377 |
|
|
unsigned long flags;
|
378 |
|
|
int retval;
|
379 |
|
|
|
380 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
381 |
|
|
retval = sprintf(buf, "%s\n", &hvcsd->p_location_code[0]);
|
382 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
383 |
|
|
return retval;
|
384 |
|
|
}
|
385 |
|
|
static DEVICE_ATTR(partner_clcs, S_IRUGO, hvcs_partner_clcs_show, NULL);
|
386 |
|
|
|
387 |
|
|
static ssize_t hvcs_current_vty_store(struct device *dev, struct device_attribute *attr, const char * buf,
|
388 |
|
|
size_t count)
|
389 |
|
|
{
|
390 |
|
|
/*
|
391 |
|
|
* Don't need this feature at the present time because firmware doesn't
|
392 |
|
|
* yet support multiple partners.
|
393 |
|
|
*/
|
394 |
|
|
printk(KERN_INFO "HVCS: Denied current_vty change: -EPERM.\n");
|
395 |
|
|
return -EPERM;
|
396 |
|
|
}
|
397 |
|
|
|
398 |
|
|
static ssize_t hvcs_current_vty_show(struct device *dev, struct device_attribute *attr, char *buf)
|
399 |
|
|
{
|
400 |
|
|
struct vio_dev *viod = to_vio_dev(dev);
|
401 |
|
|
struct hvcs_struct *hvcsd = from_vio_dev(viod);
|
402 |
|
|
unsigned long flags;
|
403 |
|
|
int retval;
|
404 |
|
|
|
405 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
406 |
|
|
retval = sprintf(buf, "%s\n", &hvcsd->p_location_code[0]);
|
407 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
408 |
|
|
return retval;
|
409 |
|
|
}
|
410 |
|
|
|
411 |
|
|
static DEVICE_ATTR(current_vty,
|
412 |
|
|
S_IRUGO | S_IWUSR, hvcs_current_vty_show, hvcs_current_vty_store);
|
413 |
|
|
|
414 |
|
|
static ssize_t hvcs_vterm_state_store(struct device *dev, struct device_attribute *attr, const char *buf,
|
415 |
|
|
size_t count)
|
416 |
|
|
{
|
417 |
|
|
struct vio_dev *viod = to_vio_dev(dev);
|
418 |
|
|
struct hvcs_struct *hvcsd = from_vio_dev(viod);
|
419 |
|
|
unsigned long flags;
|
420 |
|
|
|
421 |
|
|
/* writing a '0' to this sysfs entry will result in the disconnect. */
|
422 |
|
|
if (simple_strtol(buf, NULL, 0) != 0)
|
423 |
|
|
return -EINVAL;
|
424 |
|
|
|
425 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
426 |
|
|
|
427 |
|
|
if (hvcsd->open_count > 0) {
|
428 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
429 |
|
|
printk(KERN_INFO "HVCS: vterm state unchanged. "
|
430 |
|
|
"The hvcs device node is still in use.\n");
|
431 |
|
|
return -EPERM;
|
432 |
|
|
}
|
433 |
|
|
|
434 |
|
|
if (hvcsd->connected == 0) {
|
435 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
436 |
|
|
printk(KERN_INFO "HVCS: vterm state unchanged. The"
|
437 |
|
|
" vty-server is not connected to a vty.\n");
|
438 |
|
|
return -EPERM;
|
439 |
|
|
}
|
440 |
|
|
|
441 |
|
|
hvcs_partner_free(hvcsd);
|
442 |
|
|
printk(KERN_INFO "HVCS: Closed vty-server@%X and"
|
443 |
|
|
" partner vty@%X:%d connection.\n",
|
444 |
|
|
hvcsd->vdev->unit_address,
|
445 |
|
|
hvcsd->p_unit_address,
|
446 |
|
|
(uint32_t)hvcsd->p_partition_ID);
|
447 |
|
|
|
448 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
449 |
|
|
return count;
|
450 |
|
|
}
|
451 |
|
|
|
452 |
|
|
static ssize_t hvcs_vterm_state_show(struct device *dev, struct device_attribute *attr, char *buf)
|
453 |
|
|
{
|
454 |
|
|
struct vio_dev *viod = to_vio_dev(dev);
|
455 |
|
|
struct hvcs_struct *hvcsd = from_vio_dev(viod);
|
456 |
|
|
unsigned long flags;
|
457 |
|
|
int retval;
|
458 |
|
|
|
459 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
460 |
|
|
retval = sprintf(buf, "%d\n", hvcsd->connected);
|
461 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
462 |
|
|
return retval;
|
463 |
|
|
}
|
464 |
|
|
static DEVICE_ATTR(vterm_state, S_IRUGO | S_IWUSR,
|
465 |
|
|
hvcs_vterm_state_show, hvcs_vterm_state_store);
|
466 |
|
|
|
467 |
|
|
static ssize_t hvcs_index_show(struct device *dev, struct device_attribute *attr, char *buf)
|
468 |
|
|
{
|
469 |
|
|
struct vio_dev *viod = to_vio_dev(dev);
|
470 |
|
|
struct hvcs_struct *hvcsd = from_vio_dev(viod);
|
471 |
|
|
unsigned long flags;
|
472 |
|
|
int retval;
|
473 |
|
|
|
474 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
475 |
|
|
retval = sprintf(buf, "%d\n", hvcsd->index);
|
476 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
477 |
|
|
return retval;
|
478 |
|
|
}
|
479 |
|
|
|
480 |
|
|
static DEVICE_ATTR(index, S_IRUGO, hvcs_index_show, NULL);
|
481 |
|
|
|
482 |
|
|
static struct attribute *hvcs_attrs[] = {
|
483 |
|
|
&dev_attr_partner_vtys.attr,
|
484 |
|
|
&dev_attr_partner_clcs.attr,
|
485 |
|
|
&dev_attr_current_vty.attr,
|
486 |
|
|
&dev_attr_vterm_state.attr,
|
487 |
|
|
&dev_attr_index.attr,
|
488 |
|
|
NULL,
|
489 |
|
|
};
|
490 |
|
|
|
491 |
|
|
static struct attribute_group hvcs_attr_group = {
|
492 |
|
|
.attrs = hvcs_attrs,
|
493 |
|
|
};
|
494 |
|
|
|
495 |
|
|
static ssize_t hvcs_rescan_show(struct device_driver *ddp, char *buf)
|
496 |
|
|
{
|
497 |
|
|
/* A 1 means it is updating, a 0 means it is done updating */
|
498 |
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", hvcs_rescan_status);
|
499 |
|
|
}
|
500 |
|
|
|
501 |
|
|
static ssize_t hvcs_rescan_store(struct device_driver *ddp, const char * buf,
|
502 |
|
|
size_t count)
|
503 |
|
|
{
|
504 |
|
|
if ((simple_strtol(buf, NULL, 0) != 1)
|
505 |
|
|
&& (hvcs_rescan_status != 0))
|
506 |
|
|
return -EINVAL;
|
507 |
|
|
|
508 |
|
|
hvcs_rescan_status = 1;
|
509 |
|
|
printk(KERN_INFO "HVCS: rescanning partner info for all"
|
510 |
|
|
" vty-servers.\n");
|
511 |
|
|
hvcs_rescan_devices_list();
|
512 |
|
|
hvcs_rescan_status = 0;
|
513 |
|
|
return count;
|
514 |
|
|
}
|
515 |
|
|
|
516 |
|
|
static DRIVER_ATTR(rescan,
|
517 |
|
|
S_IRUGO | S_IWUSR, hvcs_rescan_show, hvcs_rescan_store);
|
518 |
|
|
|
519 |
|
|
static void hvcs_kick(void)
|
520 |
|
|
{
|
521 |
|
|
hvcs_kicked = 1;
|
522 |
|
|
wmb();
|
523 |
|
|
wake_up_process(hvcs_task);
|
524 |
|
|
}
|
525 |
|
|
|
526 |
|
|
static void hvcs_unthrottle(struct tty_struct *tty)
|
527 |
|
|
{
|
528 |
|
|
struct hvcs_struct *hvcsd = tty->driver_data;
|
529 |
|
|
unsigned long flags;
|
530 |
|
|
|
531 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
532 |
|
|
hvcsd->todo_mask |= HVCS_SCHED_READ;
|
533 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
534 |
|
|
hvcs_kick();
|
535 |
|
|
}
|
536 |
|
|
|
537 |
|
|
static void hvcs_throttle(struct tty_struct *tty)
|
538 |
|
|
{
|
539 |
|
|
struct hvcs_struct *hvcsd = tty->driver_data;
|
540 |
|
|
unsigned long flags;
|
541 |
|
|
|
542 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
543 |
|
|
vio_disable_interrupts(hvcsd->vdev);
|
544 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
545 |
|
|
}
|
546 |
|
|
|
547 |
|
|
/*
|
548 |
|
|
* If the device is being removed we don't have to worry about this interrupt
|
549 |
|
|
* handler taking any further interrupts because they are disabled which means
|
550 |
|
|
* the hvcs_struct will always be valid in this handler.
|
551 |
|
|
*/
|
552 |
|
|
static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance)
|
553 |
|
|
{
|
554 |
|
|
struct hvcs_struct *hvcsd = dev_instance;
|
555 |
|
|
|
556 |
|
|
spin_lock(&hvcsd->lock);
|
557 |
|
|
vio_disable_interrupts(hvcsd->vdev);
|
558 |
|
|
hvcsd->todo_mask |= HVCS_SCHED_READ;
|
559 |
|
|
spin_unlock(&hvcsd->lock);
|
560 |
|
|
hvcs_kick();
|
561 |
|
|
|
562 |
|
|
return IRQ_HANDLED;
|
563 |
|
|
}
|
564 |
|
|
|
565 |
|
|
/* This function must be called with the hvcsd->lock held */
|
566 |
|
|
static void hvcs_try_write(struct hvcs_struct *hvcsd)
|
567 |
|
|
{
|
568 |
|
|
uint32_t unit_address = hvcsd->vdev->unit_address;
|
569 |
|
|
struct tty_struct *tty = hvcsd->tty;
|
570 |
|
|
int sent;
|
571 |
|
|
|
572 |
|
|
if (hvcsd->todo_mask & HVCS_TRY_WRITE) {
|
573 |
|
|
/* won't send partial writes */
|
574 |
|
|
sent = hvc_put_chars(unit_address,
|
575 |
|
|
&hvcsd->buffer[0],
|
576 |
|
|
hvcsd->chars_in_buffer );
|
577 |
|
|
if (sent > 0) {
|
578 |
|
|
hvcsd->chars_in_buffer = 0;
|
579 |
|
|
/* wmb(); */
|
580 |
|
|
hvcsd->todo_mask &= ~(HVCS_TRY_WRITE);
|
581 |
|
|
/* wmb(); */
|
582 |
|
|
|
583 |
|
|
/*
|
584 |
|
|
* We are still obligated to deliver the data to the
|
585 |
|
|
* hypervisor even if the tty has been closed because
|
586 |
|
|
* we commited to delivering it. But don't try to wake
|
587 |
|
|
* a non-existent tty.
|
588 |
|
|
*/
|
589 |
|
|
if (tty) {
|
590 |
|
|
tty_wakeup(tty);
|
591 |
|
|
}
|
592 |
|
|
}
|
593 |
|
|
}
|
594 |
|
|
}
|
595 |
|
|
|
596 |
|
|
static int hvcs_io(struct hvcs_struct *hvcsd)
|
597 |
|
|
{
|
598 |
|
|
uint32_t unit_address;
|
599 |
|
|
struct tty_struct *tty;
|
600 |
|
|
char buf[HVCS_BUFF_LEN] __ALIGNED__;
|
601 |
|
|
unsigned long flags;
|
602 |
|
|
int got = 0;
|
603 |
|
|
|
604 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
605 |
|
|
|
606 |
|
|
unit_address = hvcsd->vdev->unit_address;
|
607 |
|
|
tty = hvcsd->tty;
|
608 |
|
|
|
609 |
|
|
hvcs_try_write(hvcsd);
|
610 |
|
|
|
611 |
|
|
if (!tty || test_bit(TTY_THROTTLED, &tty->flags)) {
|
612 |
|
|
hvcsd->todo_mask &= ~(HVCS_READ_MASK);
|
613 |
|
|
goto bail;
|
614 |
|
|
} else if (!(hvcsd->todo_mask & (HVCS_READ_MASK)))
|
615 |
|
|
goto bail;
|
616 |
|
|
|
617 |
|
|
/* remove the read masks */
|
618 |
|
|
hvcsd->todo_mask &= ~(HVCS_READ_MASK);
|
619 |
|
|
|
620 |
|
|
if (tty_buffer_request_room(tty, HVCS_BUFF_LEN) >= HVCS_BUFF_LEN) {
|
621 |
|
|
got = hvc_get_chars(unit_address,
|
622 |
|
|
&buf[0],
|
623 |
|
|
HVCS_BUFF_LEN);
|
624 |
|
|
tty_insert_flip_string(tty, buf, got);
|
625 |
|
|
}
|
626 |
|
|
|
627 |
|
|
/* Give the TTY time to process the data we just sent. */
|
628 |
|
|
if (got)
|
629 |
|
|
hvcsd->todo_mask |= HVCS_QUICK_READ;
|
630 |
|
|
|
631 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
632 |
|
|
/* This is synch because tty->low_latency == 1 */
|
633 |
|
|
if(got)
|
634 |
|
|
tty_flip_buffer_push(tty);
|
635 |
|
|
|
636 |
|
|
if (!got) {
|
637 |
|
|
/* Do this _after_ the flip_buffer_push */
|
638 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
639 |
|
|
vio_enable_interrupts(hvcsd->vdev);
|
640 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
641 |
|
|
}
|
642 |
|
|
|
643 |
|
|
return hvcsd->todo_mask;
|
644 |
|
|
|
645 |
|
|
bail:
|
646 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
647 |
|
|
return hvcsd->todo_mask;
|
648 |
|
|
}
|
649 |
|
|
|
650 |
|
|
static int khvcsd(void *unused)
|
651 |
|
|
{
|
652 |
|
|
struct hvcs_struct *hvcsd;
|
653 |
|
|
int hvcs_todo_mask;
|
654 |
|
|
|
655 |
|
|
__set_current_state(TASK_RUNNING);
|
656 |
|
|
|
657 |
|
|
do {
|
658 |
|
|
hvcs_todo_mask = 0;
|
659 |
|
|
hvcs_kicked = 0;
|
660 |
|
|
wmb();
|
661 |
|
|
|
662 |
|
|
spin_lock(&hvcs_structs_lock);
|
663 |
|
|
list_for_each_entry(hvcsd, &hvcs_structs, next) {
|
664 |
|
|
hvcs_todo_mask |= hvcs_io(hvcsd);
|
665 |
|
|
}
|
666 |
|
|
spin_unlock(&hvcs_structs_lock);
|
667 |
|
|
|
668 |
|
|
/*
|
669 |
|
|
* If any of the hvcs adapters want to try a write or quick read
|
670 |
|
|
* don't schedule(), yield a smidgen then execute the hvcs_io
|
671 |
|
|
* thread again for those that want the write.
|
672 |
|
|
*/
|
673 |
|
|
if (hvcs_todo_mask & (HVCS_TRY_WRITE | HVCS_QUICK_READ)) {
|
674 |
|
|
yield();
|
675 |
|
|
continue;
|
676 |
|
|
}
|
677 |
|
|
|
678 |
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
679 |
|
|
if (!hvcs_kicked)
|
680 |
|
|
schedule();
|
681 |
|
|
__set_current_state(TASK_RUNNING);
|
682 |
|
|
} while (!kthread_should_stop());
|
683 |
|
|
|
684 |
|
|
return 0;
|
685 |
|
|
}
|
686 |
|
|
|
687 |
|
|
static struct vio_device_id hvcs_driver_table[] __devinitdata= {
|
688 |
|
|
{"serial-server", "hvterm2"},
|
689 |
|
|
{ "", "" }
|
690 |
|
|
};
|
691 |
|
|
MODULE_DEVICE_TABLE(vio, hvcs_driver_table);
|
692 |
|
|
|
693 |
|
|
static void hvcs_return_index(int index)
|
694 |
|
|
{
|
695 |
|
|
/* Paranoia check */
|
696 |
|
|
if (!hvcs_index_list)
|
697 |
|
|
return;
|
698 |
|
|
if (index < 0 || index >= hvcs_index_count)
|
699 |
|
|
return;
|
700 |
|
|
if (hvcs_index_list[index] == -1)
|
701 |
|
|
return;
|
702 |
|
|
else
|
703 |
|
|
hvcs_index_list[index] = -1;
|
704 |
|
|
}
|
705 |
|
|
|
706 |
|
|
/* callback when the kboject ref count reaches zero */
|
707 |
|
|
static void destroy_hvcs_struct(struct kobject *kobj)
|
708 |
|
|
{
|
709 |
|
|
struct hvcs_struct *hvcsd = from_kobj(kobj);
|
710 |
|
|
struct vio_dev *vdev;
|
711 |
|
|
unsigned long flags;
|
712 |
|
|
|
713 |
|
|
spin_lock(&hvcs_structs_lock);
|
714 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
715 |
|
|
|
716 |
|
|
/* the list_del poisons the pointers */
|
717 |
|
|
list_del(&(hvcsd->next));
|
718 |
|
|
|
719 |
|
|
if (hvcsd->connected == 1) {
|
720 |
|
|
hvcs_partner_free(hvcsd);
|
721 |
|
|
printk(KERN_INFO "HVCS: Closed vty-server@%X and"
|
722 |
|
|
" partner vty@%X:%d connection.\n",
|
723 |
|
|
hvcsd->vdev->unit_address,
|
724 |
|
|
hvcsd->p_unit_address,
|
725 |
|
|
(uint32_t)hvcsd->p_partition_ID);
|
726 |
|
|
}
|
727 |
|
|
printk(KERN_INFO "HVCS: Destroyed hvcs_struct for vty-server@%X.\n",
|
728 |
|
|
hvcsd->vdev->unit_address);
|
729 |
|
|
|
730 |
|
|
vdev = hvcsd->vdev;
|
731 |
|
|
hvcsd->vdev = NULL;
|
732 |
|
|
|
733 |
|
|
hvcsd->p_unit_address = 0;
|
734 |
|
|
hvcsd->p_partition_ID = 0;
|
735 |
|
|
hvcs_return_index(hvcsd->index);
|
736 |
|
|
memset(&hvcsd->p_location_code[0], 0x00, HVCS_CLC_LENGTH + 1);
|
737 |
|
|
|
738 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
739 |
|
|
spin_unlock(&hvcs_structs_lock);
|
740 |
|
|
|
741 |
|
|
sysfs_remove_group(&vdev->dev.kobj, &hvcs_attr_group);
|
742 |
|
|
|
743 |
|
|
kfree(hvcsd);
|
744 |
|
|
}
|
745 |
|
|
|
746 |
|
|
static struct kobj_type hvcs_kobj_type = {
|
747 |
|
|
.release = destroy_hvcs_struct,
|
748 |
|
|
};
|
749 |
|
|
|
750 |
|
|
static int hvcs_get_index(void)
|
751 |
|
|
{
|
752 |
|
|
int i;
|
753 |
|
|
/* Paranoia check */
|
754 |
|
|
if (!hvcs_index_list) {
|
755 |
|
|
printk(KERN_ERR "HVCS: hvcs_index_list NOT valid!.\n");
|
756 |
|
|
return -EFAULT;
|
757 |
|
|
}
|
758 |
|
|
/* Find the numerically lowest first free index. */
|
759 |
|
|
for(i = 0; i < hvcs_index_count; i++) {
|
760 |
|
|
if (hvcs_index_list[i] == -1) {
|
761 |
|
|
hvcs_index_list[i] = 0;
|
762 |
|
|
return i;
|
763 |
|
|
}
|
764 |
|
|
}
|
765 |
|
|
return -1;
|
766 |
|
|
}
|
767 |
|
|
|
768 |
|
|
static int __devinit hvcs_probe(
|
769 |
|
|
struct vio_dev *dev,
|
770 |
|
|
const struct vio_device_id *id)
|
771 |
|
|
{
|
772 |
|
|
struct hvcs_struct *hvcsd;
|
773 |
|
|
int index;
|
774 |
|
|
int retval;
|
775 |
|
|
|
776 |
|
|
if (!dev || !id) {
|
777 |
|
|
printk(KERN_ERR "HVCS: probed with invalid parameter.\n");
|
778 |
|
|
return -EPERM;
|
779 |
|
|
}
|
780 |
|
|
|
781 |
|
|
/* early to avoid cleanup on failure */
|
782 |
|
|
index = hvcs_get_index();
|
783 |
|
|
if (index < 0) {
|
784 |
|
|
return -EFAULT;
|
785 |
|
|
}
|
786 |
|
|
|
787 |
|
|
hvcsd = kzalloc(sizeof(*hvcsd), GFP_KERNEL);
|
788 |
|
|
if (!hvcsd)
|
789 |
|
|
return -ENODEV;
|
790 |
|
|
|
791 |
|
|
|
792 |
|
|
spin_lock_init(&hvcsd->lock);
|
793 |
|
|
/* Automatically incs the refcount the first time */
|
794 |
|
|
kobject_init(&hvcsd->kobj);
|
795 |
|
|
/* Set up the callback for terminating the hvcs_struct's life */
|
796 |
|
|
hvcsd->kobj.ktype = &hvcs_kobj_type;
|
797 |
|
|
|
798 |
|
|
hvcsd->vdev = dev;
|
799 |
|
|
dev->dev.driver_data = hvcsd;
|
800 |
|
|
|
801 |
|
|
hvcsd->index = index;
|
802 |
|
|
|
803 |
|
|
/* hvcsd->index = ++hvcs_struct_count; */
|
804 |
|
|
hvcsd->chars_in_buffer = 0;
|
805 |
|
|
hvcsd->todo_mask = 0;
|
806 |
|
|
hvcsd->connected = 0;
|
807 |
|
|
|
808 |
|
|
/*
|
809 |
|
|
* This will populate the hvcs_struct's partner info fields for the
|
810 |
|
|
* first time.
|
811 |
|
|
*/
|
812 |
|
|
if (hvcs_get_pi(hvcsd)) {
|
813 |
|
|
printk(KERN_ERR "HVCS: Failed to fetch partner"
|
814 |
|
|
" info for vty-server@%X on device probe.\n",
|
815 |
|
|
hvcsd->vdev->unit_address);
|
816 |
|
|
}
|
817 |
|
|
|
818 |
|
|
/*
|
819 |
|
|
* If a user app opens a tty that corresponds to this vty-server before
|
820 |
|
|
* the hvcs_struct has been added to the devices list then the user app
|
821 |
|
|
* will get -ENODEV.
|
822 |
|
|
*/
|
823 |
|
|
spin_lock(&hvcs_structs_lock);
|
824 |
|
|
list_add_tail(&(hvcsd->next), &hvcs_structs);
|
825 |
|
|
spin_unlock(&hvcs_structs_lock);
|
826 |
|
|
|
827 |
|
|
retval = sysfs_create_group(&dev->dev.kobj, &hvcs_attr_group);
|
828 |
|
|
if (retval) {
|
829 |
|
|
printk(KERN_ERR "HVCS: Can't create sysfs attrs for vty-server@%X\n",
|
830 |
|
|
hvcsd->vdev->unit_address);
|
831 |
|
|
return retval;
|
832 |
|
|
}
|
833 |
|
|
|
834 |
|
|
printk(KERN_INFO "HVCS: vty-server@%X added to the vio bus.\n", dev->unit_address);
|
835 |
|
|
|
836 |
|
|
/*
|
837 |
|
|
* DON'T enable interrupts here because there is no user to receive the
|
838 |
|
|
* data.
|
839 |
|
|
*/
|
840 |
|
|
return 0;
|
841 |
|
|
}
|
842 |
|
|
|
843 |
|
|
static int __devexit hvcs_remove(struct vio_dev *dev)
|
844 |
|
|
{
|
845 |
|
|
struct hvcs_struct *hvcsd = dev->dev.driver_data;
|
846 |
|
|
unsigned long flags;
|
847 |
|
|
struct kobject *kobjp;
|
848 |
|
|
struct tty_struct *tty;
|
849 |
|
|
|
850 |
|
|
if (!hvcsd)
|
851 |
|
|
return -ENODEV;
|
852 |
|
|
|
853 |
|
|
/* By this time the vty-server won't be getting any more interrups */
|
854 |
|
|
|
855 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
856 |
|
|
|
857 |
|
|
tty = hvcsd->tty;
|
858 |
|
|
|
859 |
|
|
kobjp = &hvcsd->kobj;
|
860 |
|
|
|
861 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
862 |
|
|
|
863 |
|
|
/*
|
864 |
|
|
* Let the last holder of this object cause it to be removed, which
|
865 |
|
|
* would probably be tty_hangup below.
|
866 |
|
|
*/
|
867 |
|
|
kobject_put (kobjp);
|
868 |
|
|
|
869 |
|
|
/*
|
870 |
|
|
* The hangup is a scheduled function which will auto chain call
|
871 |
|
|
* hvcs_hangup. The tty should always be valid at this time unless a
|
872 |
|
|
* simultaneous tty close already cleaned up the hvcs_struct.
|
873 |
|
|
*/
|
874 |
|
|
if (tty)
|
875 |
|
|
tty_hangup(tty);
|
876 |
|
|
|
877 |
|
|
printk(KERN_INFO "HVCS: vty-server@%X removed from the"
|
878 |
|
|
" vio bus.\n", dev->unit_address);
|
879 |
|
|
return 0;
|
880 |
|
|
};
|
881 |
|
|
|
882 |
|
|
static struct vio_driver hvcs_vio_driver = {
|
883 |
|
|
.id_table = hvcs_driver_table,
|
884 |
|
|
.probe = hvcs_probe,
|
885 |
|
|
.remove = hvcs_remove,
|
886 |
|
|
.driver = {
|
887 |
|
|
.name = hvcs_driver_name,
|
888 |
|
|
.owner = THIS_MODULE,
|
889 |
|
|
}
|
890 |
|
|
};
|
891 |
|
|
|
892 |
|
|
/* Only called from hvcs_get_pi please */
|
893 |
|
|
static void hvcs_set_pi(struct hvcs_partner_info *pi, struct hvcs_struct *hvcsd)
|
894 |
|
|
{
|
895 |
|
|
int clclength;
|
896 |
|
|
|
897 |
|
|
hvcsd->p_unit_address = pi->unit_address;
|
898 |
|
|
hvcsd->p_partition_ID = pi->partition_ID;
|
899 |
|
|
clclength = strlen(&pi->location_code[0]);
|
900 |
|
|
if (clclength > HVCS_CLC_LENGTH)
|
901 |
|
|
clclength = HVCS_CLC_LENGTH;
|
902 |
|
|
|
903 |
|
|
/* copy the null-term char too */
|
904 |
|
|
strncpy(&hvcsd->p_location_code[0],
|
905 |
|
|
&pi->location_code[0], clclength + 1);
|
906 |
|
|
}
|
907 |
|
|
|
908 |
|
|
/*
|
909 |
|
|
* Traverse the list and add the partner info that is found to the hvcs_struct
|
910 |
|
|
* struct entry. NOTE: At this time I know that partner info will return a
|
911 |
|
|
* single entry but in the future there may be multiple partner info entries per
|
912 |
|
|
* vty-server and you'll want to zero out that list and reset it. If for some
|
913 |
|
|
* reason you have an old version of this driver but there IS more than one
|
914 |
|
|
* partner info then hvcsd->p_* will hold the last partner info data from the
|
915 |
|
|
* firmware query. A good way to update this code would be to replace the three
|
916 |
|
|
* partner info fields in hvcs_struct with a list of hvcs_partner_info
|
917 |
|
|
* instances.
|
918 |
|
|
*
|
919 |
|
|
* This function must be called with the hvcsd->lock held.
|
920 |
|
|
*/
|
921 |
|
|
static int hvcs_get_pi(struct hvcs_struct *hvcsd)
|
922 |
|
|
{
|
923 |
|
|
struct hvcs_partner_info *pi;
|
924 |
|
|
uint32_t unit_address = hvcsd->vdev->unit_address;
|
925 |
|
|
struct list_head head;
|
926 |
|
|
int retval;
|
927 |
|
|
|
928 |
|
|
spin_lock(&hvcs_pi_lock);
|
929 |
|
|
if (!hvcs_pi_buff) {
|
930 |
|
|
spin_unlock(&hvcs_pi_lock);
|
931 |
|
|
return -EFAULT;
|
932 |
|
|
}
|
933 |
|
|
retval = hvcs_get_partner_info(unit_address, &head, hvcs_pi_buff);
|
934 |
|
|
spin_unlock(&hvcs_pi_lock);
|
935 |
|
|
if (retval) {
|
936 |
|
|
printk(KERN_ERR "HVCS: Failed to fetch partner"
|
937 |
|
|
" info for vty-server@%x.\n", unit_address);
|
938 |
|
|
return retval;
|
939 |
|
|
}
|
940 |
|
|
|
941 |
|
|
/* nixes the values if the partner vty went away */
|
942 |
|
|
hvcsd->p_unit_address = 0;
|
943 |
|
|
hvcsd->p_partition_ID = 0;
|
944 |
|
|
|
945 |
|
|
list_for_each_entry(pi, &head, node)
|
946 |
|
|
hvcs_set_pi(pi, hvcsd);
|
947 |
|
|
|
948 |
|
|
hvcs_free_partner_info(&head);
|
949 |
|
|
return 0;
|
950 |
|
|
}
|
951 |
|
|
|
952 |
|
|
/*
|
953 |
|
|
* This function is executed by the driver "rescan" sysfs entry. It shouldn't
|
954 |
|
|
* be executed elsewhere, in order to prevent deadlock issues.
|
955 |
|
|
*/
|
956 |
|
|
static int hvcs_rescan_devices_list(void)
|
957 |
|
|
{
|
958 |
|
|
struct hvcs_struct *hvcsd;
|
959 |
|
|
unsigned long flags;
|
960 |
|
|
|
961 |
|
|
spin_lock(&hvcs_structs_lock);
|
962 |
|
|
|
963 |
|
|
list_for_each_entry(hvcsd, &hvcs_structs, next) {
|
964 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
965 |
|
|
hvcs_get_pi(hvcsd);
|
966 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
967 |
|
|
}
|
968 |
|
|
|
969 |
|
|
spin_unlock(&hvcs_structs_lock);
|
970 |
|
|
|
971 |
|
|
return 0;
|
972 |
|
|
}
|
973 |
|
|
|
974 |
|
|
/*
|
975 |
|
|
* Farm this off into its own function because it could be more complex once
|
976 |
|
|
* multiple partners support is added. This function should be called with
|
977 |
|
|
* the hvcsd->lock held.
|
978 |
|
|
*/
|
979 |
|
|
static int hvcs_has_pi(struct hvcs_struct *hvcsd)
|
980 |
|
|
{
|
981 |
|
|
if ((!hvcsd->p_unit_address) || (!hvcsd->p_partition_ID))
|
982 |
|
|
return 0;
|
983 |
|
|
return 1;
|
984 |
|
|
}
|
985 |
|
|
|
986 |
|
|
/*
|
987 |
|
|
* NOTE: It is possible that the super admin removed a partner vty and then
|
988 |
|
|
* added a different vty as the new partner.
|
989 |
|
|
*
|
990 |
|
|
* This function must be called with the hvcsd->lock held.
|
991 |
|
|
*/
|
992 |
|
|
static int hvcs_partner_connect(struct hvcs_struct *hvcsd)
|
993 |
|
|
{
|
994 |
|
|
int retval;
|
995 |
|
|
unsigned int unit_address = hvcsd->vdev->unit_address;
|
996 |
|
|
|
997 |
|
|
/*
|
998 |
|
|
* If there wasn't any pi when the device was added it doesn't meant
|
999 |
|
|
* there isn't any now. This driver isn't notified when a new partner
|
1000 |
|
|
* vty is added to a vty-server so we discover changes on our own.
|
1001 |
|
|
* Please see comments in hvcs_register_connection() for justification
|
1002 |
|
|
* of this bizarre code.
|
1003 |
|
|
*/
|
1004 |
|
|
retval = hvcs_register_connection(unit_address,
|
1005 |
|
|
hvcsd->p_partition_ID,
|
1006 |
|
|
hvcsd->p_unit_address);
|
1007 |
|
|
if (!retval) {
|
1008 |
|
|
hvcsd->connected = 1;
|
1009 |
|
|
return 0;
|
1010 |
|
|
} else if (retval != -EINVAL)
|
1011 |
|
|
return retval;
|
1012 |
|
|
|
1013 |
|
|
/*
|
1014 |
|
|
* As per the spec re-get the pi and try again if -EINVAL after the
|
1015 |
|
|
* first connection attempt.
|
1016 |
|
|
*/
|
1017 |
|
|
if (hvcs_get_pi(hvcsd))
|
1018 |
|
|
return -ENOMEM;
|
1019 |
|
|
|
1020 |
|
|
if (!hvcs_has_pi(hvcsd))
|
1021 |
|
|
return -ENODEV;
|
1022 |
|
|
|
1023 |
|
|
retval = hvcs_register_connection(unit_address,
|
1024 |
|
|
hvcsd->p_partition_ID,
|
1025 |
|
|
hvcsd->p_unit_address);
|
1026 |
|
|
if (retval != -EINVAL) {
|
1027 |
|
|
hvcsd->connected = 1;
|
1028 |
|
|
return retval;
|
1029 |
|
|
}
|
1030 |
|
|
|
1031 |
|
|
/*
|
1032 |
|
|
* EBUSY is the most likely scenario though the vty could have been
|
1033 |
|
|
* removed or there really could be an hcall error due to the parameter
|
1034 |
|
|
* data but thanks to ambiguous firmware return codes we can't really
|
1035 |
|
|
* tell.
|
1036 |
|
|
*/
|
1037 |
|
|
printk(KERN_INFO "HVCS: vty-server or partner"
|
1038 |
|
|
" vty is busy. Try again later.\n");
|
1039 |
|
|
return -EBUSY;
|
1040 |
|
|
}
|
1041 |
|
|
|
1042 |
|
|
/* This function must be called with the hvcsd->lock held */
|
1043 |
|
|
static void hvcs_partner_free(struct hvcs_struct *hvcsd)
|
1044 |
|
|
{
|
1045 |
|
|
int retval;
|
1046 |
|
|
do {
|
1047 |
|
|
retval = hvcs_free_connection(hvcsd->vdev->unit_address);
|
1048 |
|
|
} while (retval == -EBUSY);
|
1049 |
|
|
hvcsd->connected = 0;
|
1050 |
|
|
}
|
1051 |
|
|
|
1052 |
|
|
/* This helper function must be called WITHOUT the hvcsd->lock held */
|
1053 |
|
|
static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address,
|
1054 |
|
|
unsigned int irq, struct vio_dev *vdev)
|
1055 |
|
|
{
|
1056 |
|
|
unsigned long flags;
|
1057 |
|
|
int rc;
|
1058 |
|
|
|
1059 |
|
|
/*
|
1060 |
|
|
* It is possible that the vty-server was removed between the time that
|
1061 |
|
|
* the conn was registered and now.
|
1062 |
|
|
*/
|
1063 |
|
|
if (!(rc = request_irq(irq, &hvcs_handle_interrupt,
|
1064 |
|
|
IRQF_DISABLED, "ibmhvcs", hvcsd))) {
|
1065 |
|
|
/*
|
1066 |
|
|
* It is possible the vty-server was removed after the irq was
|
1067 |
|
|
* requested but before we have time to enable interrupts.
|
1068 |
|
|
*/
|
1069 |
|
|
if (vio_enable_interrupts(vdev) == H_SUCCESS)
|
1070 |
|
|
return 0;
|
1071 |
|
|
else {
|
1072 |
|
|
printk(KERN_ERR "HVCS: int enable failed for"
|
1073 |
|
|
" vty-server@%X.\n", unit_address);
|
1074 |
|
|
free_irq(irq, hvcsd);
|
1075 |
|
|
}
|
1076 |
|
|
} else
|
1077 |
|
|
printk(KERN_ERR "HVCS: irq req failed for"
|
1078 |
|
|
" vty-server@%X.\n", unit_address);
|
1079 |
|
|
|
1080 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
1081 |
|
|
hvcs_partner_free(hvcsd);
|
1082 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1083 |
|
|
|
1084 |
|
|
return rc;
|
1085 |
|
|
|
1086 |
|
|
}
|
1087 |
|
|
|
1088 |
|
|
/*
|
1089 |
|
|
* This always increments the kobject ref count if the call is successful.
|
1090 |
|
|
* Please remember to dec when you are done with the instance.
|
1091 |
|
|
*
|
1092 |
|
|
* NOTICE: Do NOT hold either the hvcs_struct.lock or hvcs_structs_lock when
|
1093 |
|
|
* calling this function or you will get deadlock.
|
1094 |
|
|
*/
|
1095 |
|
|
static struct hvcs_struct *hvcs_get_by_index(int index)
|
1096 |
|
|
{
|
1097 |
|
|
struct hvcs_struct *hvcsd = NULL;
|
1098 |
|
|
unsigned long flags;
|
1099 |
|
|
|
1100 |
|
|
spin_lock(&hvcs_structs_lock);
|
1101 |
|
|
/* We can immediately discard OOB requests */
|
1102 |
|
|
if (index >= 0 && index < HVCS_MAX_SERVER_ADAPTERS) {
|
1103 |
|
|
list_for_each_entry(hvcsd, &hvcs_structs, next) {
|
1104 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
1105 |
|
|
if (hvcsd->index == index) {
|
1106 |
|
|
kobject_get(&hvcsd->kobj);
|
1107 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1108 |
|
|
spin_unlock(&hvcs_structs_lock);
|
1109 |
|
|
return hvcsd;
|
1110 |
|
|
}
|
1111 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1112 |
|
|
}
|
1113 |
|
|
hvcsd = NULL;
|
1114 |
|
|
}
|
1115 |
|
|
|
1116 |
|
|
spin_unlock(&hvcs_structs_lock);
|
1117 |
|
|
return hvcsd;
|
1118 |
|
|
}
|
1119 |
|
|
|
1120 |
|
|
/*
|
1121 |
|
|
* This is invoked via the tty_open interface when a user app connects to the
|
1122 |
|
|
* /dev node.
|
1123 |
|
|
*/
|
1124 |
|
|
static int hvcs_open(struct tty_struct *tty, struct file *filp)
|
1125 |
|
|
{
|
1126 |
|
|
struct hvcs_struct *hvcsd;
|
1127 |
|
|
int rc, retval = 0;
|
1128 |
|
|
unsigned long flags;
|
1129 |
|
|
unsigned int irq;
|
1130 |
|
|
struct vio_dev *vdev;
|
1131 |
|
|
unsigned long unit_address;
|
1132 |
|
|
struct kobject *kobjp;
|
1133 |
|
|
|
1134 |
|
|
if (tty->driver_data)
|
1135 |
|
|
goto fast_open;
|
1136 |
|
|
|
1137 |
|
|
/*
|
1138 |
|
|
* Is there a vty-server that shares the same index?
|
1139 |
|
|
* This function increments the kobject index.
|
1140 |
|
|
*/
|
1141 |
|
|
if (!(hvcsd = hvcs_get_by_index(tty->index))) {
|
1142 |
|
|
printk(KERN_WARNING "HVCS: open failed, no device associated"
|
1143 |
|
|
" with tty->index %d.\n", tty->index);
|
1144 |
|
|
return -ENODEV;
|
1145 |
|
|
}
|
1146 |
|
|
|
1147 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
1148 |
|
|
|
1149 |
|
|
if (hvcsd->connected == 0)
|
1150 |
|
|
if ((retval = hvcs_partner_connect(hvcsd)))
|
1151 |
|
|
goto error_release;
|
1152 |
|
|
|
1153 |
|
|
hvcsd->open_count = 1;
|
1154 |
|
|
hvcsd->tty = tty;
|
1155 |
|
|
tty->driver_data = hvcsd;
|
1156 |
|
|
|
1157 |
|
|
/*
|
1158 |
|
|
* Set this driver to low latency so that we actually have a chance at
|
1159 |
|
|
* catching a throttled TTY after we flip_buffer_push. Otherwise the
|
1160 |
|
|
* flush_to_async may not execute until after the kernel_thread has
|
1161 |
|
|
* yielded and resumed the next flip_buffer_push resulting in data
|
1162 |
|
|
* loss.
|
1163 |
|
|
*/
|
1164 |
|
|
tty->low_latency = 1;
|
1165 |
|
|
|
1166 |
|
|
memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN);
|
1167 |
|
|
|
1168 |
|
|
/*
|
1169 |
|
|
* Save these in the spinlock for the enable operations that need them
|
1170 |
|
|
* outside of the spinlock.
|
1171 |
|
|
*/
|
1172 |
|
|
irq = hvcsd->vdev->irq;
|
1173 |
|
|
vdev = hvcsd->vdev;
|
1174 |
|
|
unit_address = hvcsd->vdev->unit_address;
|
1175 |
|
|
|
1176 |
|
|
hvcsd->todo_mask |= HVCS_SCHED_READ;
|
1177 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1178 |
|
|
|
1179 |
|
|
/*
|
1180 |
|
|
* This must be done outside of the spinlock because it requests irqs
|
1181 |
|
|
* and will grab the spinlock and free the connection if it fails.
|
1182 |
|
|
*/
|
1183 |
|
|
if (((rc = hvcs_enable_device(hvcsd, unit_address, irq, vdev)))) {
|
1184 |
|
|
kobject_put(&hvcsd->kobj);
|
1185 |
|
|
printk(KERN_WARNING "HVCS: enable device failed.\n");
|
1186 |
|
|
return rc;
|
1187 |
|
|
}
|
1188 |
|
|
|
1189 |
|
|
goto open_success;
|
1190 |
|
|
|
1191 |
|
|
fast_open:
|
1192 |
|
|
hvcsd = tty->driver_data;
|
1193 |
|
|
|
1194 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
1195 |
|
|
if (!kobject_get(&hvcsd->kobj)) {
|
1196 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1197 |
|
|
printk(KERN_ERR "HVCS: Kobject of open"
|
1198 |
|
|
" hvcs doesn't exist.\n");
|
1199 |
|
|
return -EFAULT; /* Is this the right return value? */
|
1200 |
|
|
}
|
1201 |
|
|
|
1202 |
|
|
hvcsd->open_count++;
|
1203 |
|
|
|
1204 |
|
|
hvcsd->todo_mask |= HVCS_SCHED_READ;
|
1205 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1206 |
|
|
open_success:
|
1207 |
|
|
hvcs_kick();
|
1208 |
|
|
|
1209 |
|
|
printk(KERN_INFO "HVCS: vty-server@%X connection opened.\n",
|
1210 |
|
|
hvcsd->vdev->unit_address );
|
1211 |
|
|
|
1212 |
|
|
return 0;
|
1213 |
|
|
|
1214 |
|
|
error_release:
|
1215 |
|
|
kobjp = &hvcsd->kobj;
|
1216 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1217 |
|
|
kobject_put(&hvcsd->kobj);
|
1218 |
|
|
|
1219 |
|
|
printk(KERN_WARNING "HVCS: partner connect failed.\n");
|
1220 |
|
|
return retval;
|
1221 |
|
|
}
|
1222 |
|
|
|
1223 |
|
|
static void hvcs_close(struct tty_struct *tty, struct file *filp)
|
1224 |
|
|
{
|
1225 |
|
|
struct hvcs_struct *hvcsd;
|
1226 |
|
|
unsigned long flags;
|
1227 |
|
|
struct kobject *kobjp;
|
1228 |
|
|
int irq = NO_IRQ;
|
1229 |
|
|
|
1230 |
|
|
/*
|
1231 |
|
|
* Is someone trying to close the file associated with this device after
|
1232 |
|
|
* we have hung up? If so tty->driver_data wouldn't be valid.
|
1233 |
|
|
*/
|
1234 |
|
|
if (tty_hung_up_p(filp))
|
1235 |
|
|
return;
|
1236 |
|
|
|
1237 |
|
|
/*
|
1238 |
|
|
* No driver_data means that this close was probably issued after a
|
1239 |
|
|
* failed hvcs_open by the tty layer's release_dev() api and we can just
|
1240 |
|
|
* exit cleanly.
|
1241 |
|
|
*/
|
1242 |
|
|
if (!tty->driver_data)
|
1243 |
|
|
return;
|
1244 |
|
|
|
1245 |
|
|
hvcsd = tty->driver_data;
|
1246 |
|
|
|
1247 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
1248 |
|
|
kobjp = &hvcsd->kobj;
|
1249 |
|
|
if (--hvcsd->open_count == 0) {
|
1250 |
|
|
|
1251 |
|
|
vio_disable_interrupts(hvcsd->vdev);
|
1252 |
|
|
|
1253 |
|
|
/*
|
1254 |
|
|
* NULL this early so that the kernel_thread doesn't try to
|
1255 |
|
|
* execute any operations on the TTY even though it is obligated
|
1256 |
|
|
* to deliver any pending I/O to the hypervisor.
|
1257 |
|
|
*/
|
1258 |
|
|
hvcsd->tty = NULL;
|
1259 |
|
|
|
1260 |
|
|
irq = hvcsd->vdev->irq;
|
1261 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1262 |
|
|
|
1263 |
|
|
tty_wait_until_sent(tty, HVCS_CLOSE_WAIT);
|
1264 |
|
|
|
1265 |
|
|
/*
|
1266 |
|
|
* This line is important because it tells hvcs_open that this
|
1267 |
|
|
* device needs to be re-configured the next time hvcs_open is
|
1268 |
|
|
* called.
|
1269 |
|
|
*/
|
1270 |
|
|
tty->driver_data = NULL;
|
1271 |
|
|
|
1272 |
|
|
free_irq(irq, hvcsd);
|
1273 |
|
|
kobject_put(kobjp);
|
1274 |
|
|
return;
|
1275 |
|
|
} else if (hvcsd->open_count < 0) {
|
1276 |
|
|
printk(KERN_ERR "HVCS: vty-server@%X open_count: %d"
|
1277 |
|
|
" is missmanaged.\n",
|
1278 |
|
|
hvcsd->vdev->unit_address, hvcsd->open_count);
|
1279 |
|
|
}
|
1280 |
|
|
|
1281 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1282 |
|
|
kobject_put(kobjp);
|
1283 |
|
|
}
|
1284 |
|
|
|
1285 |
|
|
static void hvcs_hangup(struct tty_struct * tty)
|
1286 |
|
|
{
|
1287 |
|
|
struct hvcs_struct *hvcsd = tty->driver_data;
|
1288 |
|
|
unsigned long flags;
|
1289 |
|
|
int temp_open_count;
|
1290 |
|
|
struct kobject *kobjp;
|
1291 |
|
|
int irq = NO_IRQ;
|
1292 |
|
|
|
1293 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
1294 |
|
|
/* Preserve this so that we know how many kobject refs to put */
|
1295 |
|
|
temp_open_count = hvcsd->open_count;
|
1296 |
|
|
|
1297 |
|
|
/*
|
1298 |
|
|
* Don't kobject put inside the spinlock because the destruction
|
1299 |
|
|
* callback may use the spinlock and it may get called before the
|
1300 |
|
|
* spinlock has been released. Get a pointer to the kobject and
|
1301 |
|
|
* kobject_put on that after releasing the spinlock.
|
1302 |
|
|
*/
|
1303 |
|
|
kobjp = &hvcsd->kobj;
|
1304 |
|
|
|
1305 |
|
|
vio_disable_interrupts(hvcsd->vdev);
|
1306 |
|
|
|
1307 |
|
|
hvcsd->todo_mask = 0;
|
1308 |
|
|
|
1309 |
|
|
/* I don't think the tty needs the hvcs_struct pointer after a hangup */
|
1310 |
|
|
hvcsd->tty->driver_data = NULL;
|
1311 |
|
|
hvcsd->tty = NULL;
|
1312 |
|
|
|
1313 |
|
|
hvcsd->open_count = 0;
|
1314 |
|
|
|
1315 |
|
|
/* This will drop any buffered data on the floor which is OK in a hangup
|
1316 |
|
|
* scenario. */
|
1317 |
|
|
memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN);
|
1318 |
|
|
hvcsd->chars_in_buffer = 0;
|
1319 |
|
|
|
1320 |
|
|
irq = hvcsd->vdev->irq;
|
1321 |
|
|
|
1322 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1323 |
|
|
|
1324 |
|
|
free_irq(irq, hvcsd);
|
1325 |
|
|
|
1326 |
|
|
/*
|
1327 |
|
|
* We need to kobject_put() for every open_count we have since the
|
1328 |
|
|
* tty_hangup() function doesn't invoke a close per open connection on a
|
1329 |
|
|
* non-console device.
|
1330 |
|
|
*/
|
1331 |
|
|
while(temp_open_count) {
|
1332 |
|
|
--temp_open_count;
|
1333 |
|
|
/*
|
1334 |
|
|
* The final put will trigger destruction of the hvcs_struct.
|
1335 |
|
|
* NOTE: If this hangup was signaled from user space then the
|
1336 |
|
|
* final put will never happen.
|
1337 |
|
|
*/
|
1338 |
|
|
kobject_put(kobjp);
|
1339 |
|
|
}
|
1340 |
|
|
}
|
1341 |
|
|
|
1342 |
|
|
/*
|
1343 |
|
|
* NOTE: This is almost always from_user since user level apps interact with the
|
1344 |
|
|
* /dev nodes. I'm trusting that if hvcs_write gets called and interrupted by
|
1345 |
|
|
* hvcs_remove (which removes the target device and executes tty_hangup()) that
|
1346 |
|
|
* tty_hangup will allow hvcs_write time to complete execution before it
|
1347 |
|
|
* terminates our device.
|
1348 |
|
|
*/
|
1349 |
|
|
static int hvcs_write(struct tty_struct *tty,
|
1350 |
|
|
const unsigned char *buf, int count)
|
1351 |
|
|
{
|
1352 |
|
|
struct hvcs_struct *hvcsd = tty->driver_data;
|
1353 |
|
|
unsigned int unit_address;
|
1354 |
|
|
const unsigned char *charbuf;
|
1355 |
|
|
unsigned long flags;
|
1356 |
|
|
int total_sent = 0;
|
1357 |
|
|
int tosend = 0;
|
1358 |
|
|
int result = 0;
|
1359 |
|
|
|
1360 |
|
|
/*
|
1361 |
|
|
* If they don't check the return code off of their open they may
|
1362 |
|
|
* attempt this even if there is no connected device.
|
1363 |
|
|
*/
|
1364 |
|
|
if (!hvcsd)
|
1365 |
|
|
return -ENODEV;
|
1366 |
|
|
|
1367 |
|
|
/* Reasonable size to prevent user level flooding */
|
1368 |
|
|
if (count > HVCS_MAX_FROM_USER) {
|
1369 |
|
|
printk(KERN_WARNING "HVCS write: count being truncated to"
|
1370 |
|
|
" HVCS_MAX_FROM_USER.\n");
|
1371 |
|
|
count = HVCS_MAX_FROM_USER;
|
1372 |
|
|
}
|
1373 |
|
|
|
1374 |
|
|
charbuf = buf;
|
1375 |
|
|
|
1376 |
|
|
spin_lock_irqsave(&hvcsd->lock, flags);
|
1377 |
|
|
|
1378 |
|
|
/*
|
1379 |
|
|
* Somehow an open succedded but the device was removed or the
|
1380 |
|
|
* connection terminated between the vty-server and partner vty during
|
1381 |
|
|
* the middle of a write operation? This is a crummy place to do this
|
1382 |
|
|
* but we want to keep it all in the spinlock.
|
1383 |
|
|
*/
|
1384 |
|
|
if (hvcsd->open_count <= 0) {
|
1385 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1386 |
|
|
return -ENODEV;
|
1387 |
|
|
}
|
1388 |
|
|
|
1389 |
|
|
unit_address = hvcsd->vdev->unit_address;
|
1390 |
|
|
|
1391 |
|
|
while (count > 0) {
|
1392 |
|
|
tosend = min(count, (HVCS_BUFF_LEN - hvcsd->chars_in_buffer));
|
1393 |
|
|
/*
|
1394 |
|
|
* No more space, this probably means that the last call to
|
1395 |
|
|
* hvcs_write() didn't succeed and the buffer was filled up.
|
1396 |
|
|
*/
|
1397 |
|
|
if (!tosend)
|
1398 |
|
|
break;
|
1399 |
|
|
|
1400 |
|
|
memcpy(&hvcsd->buffer[hvcsd->chars_in_buffer],
|
1401 |
|
|
&charbuf[total_sent],
|
1402 |
|
|
tosend);
|
1403 |
|
|
|
1404 |
|
|
hvcsd->chars_in_buffer += tosend;
|
1405 |
|
|
|
1406 |
|
|
result = 0;
|
1407 |
|
|
|
1408 |
|
|
/*
|
1409 |
|
|
* If this is true then we don't want to try writing to the
|
1410 |
|
|
* hypervisor because that is the kernel_threads job now. We'll
|
1411 |
|
|
* just add to the buffer.
|
1412 |
|
|
*/
|
1413 |
|
|
if (!(hvcsd->todo_mask & HVCS_TRY_WRITE))
|
1414 |
|
|
/* won't send partial writes */
|
1415 |
|
|
result = hvc_put_chars(unit_address,
|
1416 |
|
|
&hvcsd->buffer[0],
|
1417 |
|
|
hvcsd->chars_in_buffer);
|
1418 |
|
|
|
1419 |
|
|
/*
|
1420 |
|
|
* Since we know we have enough room in hvcsd->buffer for
|
1421 |
|
|
* tosend we record that it was sent regardless of whether the
|
1422 |
|
|
* hypervisor actually took it because we have it buffered.
|
1423 |
|
|
*/
|
1424 |
|
|
total_sent+=tosend;
|
1425 |
|
|
count-=tosend;
|
1426 |
|
|
if (result == 0) {
|
1427 |
|
|
hvcsd->todo_mask |= HVCS_TRY_WRITE;
|
1428 |
|
|
hvcs_kick();
|
1429 |
|
|
break;
|
1430 |
|
|
}
|
1431 |
|
|
|
1432 |
|
|
hvcsd->chars_in_buffer = 0;
|
1433 |
|
|
/*
|
1434 |
|
|
* Test after the chars_in_buffer reset otherwise this could
|
1435 |
|
|
* deadlock our writes if hvc_put_chars fails.
|
1436 |
|
|
*/
|
1437 |
|
|
if (result < 0)
|
1438 |
|
|
break;
|
1439 |
|
|
}
|
1440 |
|
|
|
1441 |
|
|
spin_unlock_irqrestore(&hvcsd->lock, flags);
|
1442 |
|
|
|
1443 |
|
|
if (result == -1)
|
1444 |
|
|
return -EIO;
|
1445 |
|
|
else
|
1446 |
|
|
return total_sent;
|
1447 |
|
|
}
|
1448 |
|
|
|
1449 |
|
|
/*
|
1450 |
|
|
* This is really asking how much can we guarentee that we can send or that we
|
1451 |
|
|
* absolutely WILL BUFFER if we can't send it. This driver MUST honor the
|
1452 |
|
|
* return value, hence the reason for hvcs_struct buffering.
|
1453 |
|
|
*/
|
1454 |
|
|
static int hvcs_write_room(struct tty_struct *tty)
|
1455 |
|
|
{
|
1456 |
|
|
struct hvcs_struct *hvcsd = tty->driver_data;
|
1457 |
|
|
|
1458 |
|
|
if (!hvcsd || hvcsd->open_count <= 0)
|
1459 |
|
|
return 0;
|
1460 |
|
|
|
1461 |
|
|
return HVCS_BUFF_LEN - hvcsd->chars_in_buffer;
|
1462 |
|
|
}
|
1463 |
|
|
|
1464 |
|
|
static int hvcs_chars_in_buffer(struct tty_struct *tty)
|
1465 |
|
|
{
|
1466 |
|
|
struct hvcs_struct *hvcsd = tty->driver_data;
|
1467 |
|
|
|
1468 |
|
|
return hvcsd->chars_in_buffer;
|
1469 |
|
|
}
|
1470 |
|
|
|
1471 |
|
|
static const struct tty_operations hvcs_ops = {
|
1472 |
|
|
.open = hvcs_open,
|
1473 |
|
|
.close = hvcs_close,
|
1474 |
|
|
.hangup = hvcs_hangup,
|
1475 |
|
|
.write = hvcs_write,
|
1476 |
|
|
.write_room = hvcs_write_room,
|
1477 |
|
|
.chars_in_buffer = hvcs_chars_in_buffer,
|
1478 |
|
|
.unthrottle = hvcs_unthrottle,
|
1479 |
|
|
.throttle = hvcs_throttle,
|
1480 |
|
|
};
|
1481 |
|
|
|
1482 |
|
|
static int hvcs_alloc_index_list(int n)
|
1483 |
|
|
{
|
1484 |
|
|
int i;
|
1485 |
|
|
|
1486 |
|
|
hvcs_index_list = kmalloc(n * sizeof(hvcs_index_count),GFP_KERNEL);
|
1487 |
|
|
if (!hvcs_index_list)
|
1488 |
|
|
return -ENOMEM;
|
1489 |
|
|
hvcs_index_count = n;
|
1490 |
|
|
for (i = 0; i < hvcs_index_count; i++)
|
1491 |
|
|
hvcs_index_list[i] = -1;
|
1492 |
|
|
return 0;
|
1493 |
|
|
}
|
1494 |
|
|
|
1495 |
|
|
static void hvcs_free_index_list(void)
|
1496 |
|
|
{
|
1497 |
|
|
/* Paranoia check to be thorough. */
|
1498 |
|
|
kfree(hvcs_index_list);
|
1499 |
|
|
hvcs_index_list = NULL;
|
1500 |
|
|
hvcs_index_count = 0;
|
1501 |
|
|
}
|
1502 |
|
|
|
1503 |
|
|
static int __init hvcs_module_init(void)
|
1504 |
|
|
{
|
1505 |
|
|
int rc;
|
1506 |
|
|
int num_ttys_to_alloc;
|
1507 |
|
|
|
1508 |
|
|
printk(KERN_INFO "Initializing %s\n", hvcs_driver_string);
|
1509 |
|
|
|
1510 |
|
|
/* Has the user specified an overload with an insmod param? */
|
1511 |
|
|
if (hvcs_parm_num_devs <= 0 ||
|
1512 |
|
|
(hvcs_parm_num_devs > HVCS_MAX_SERVER_ADAPTERS)) {
|
1513 |
|
|
num_ttys_to_alloc = HVCS_DEFAULT_SERVER_ADAPTERS;
|
1514 |
|
|
} else
|
1515 |
|
|
num_ttys_to_alloc = hvcs_parm_num_devs;
|
1516 |
|
|
|
1517 |
|
|
hvcs_tty_driver = alloc_tty_driver(num_ttys_to_alloc);
|
1518 |
|
|
if (!hvcs_tty_driver)
|
1519 |
|
|
return -ENOMEM;
|
1520 |
|
|
|
1521 |
|
|
if (hvcs_alloc_index_list(num_ttys_to_alloc)) {
|
1522 |
|
|
rc = -ENOMEM;
|
1523 |
|
|
goto index_fail;
|
1524 |
|
|
}
|
1525 |
|
|
|
1526 |
|
|
hvcs_tty_driver->owner = THIS_MODULE;
|
1527 |
|
|
|
1528 |
|
|
hvcs_tty_driver->driver_name = hvcs_driver_name;
|
1529 |
|
|
hvcs_tty_driver->name = hvcs_device_node;
|
1530 |
|
|
|
1531 |
|
|
/*
|
1532 |
|
|
* We'll let the system assign us a major number, indicated by leaving
|
1533 |
|
|
* it blank.
|
1534 |
|
|
*/
|
1535 |
|
|
|
1536 |
|
|
hvcs_tty_driver->minor_start = HVCS_MINOR_START;
|
1537 |
|
|
hvcs_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM;
|
1538 |
|
|
|
1539 |
|
|
/*
|
1540 |
|
|
* We role our own so that we DONT ECHO. We can't echo because the
|
1541 |
|
|
* device we are connecting to already echoes by default and this would
|
1542 |
|
|
* throw us into a horrible recursive echo-echo-echo loop.
|
1543 |
|
|
*/
|
1544 |
|
|
hvcs_tty_driver->init_termios = hvcs_tty_termios;
|
1545 |
|
|
hvcs_tty_driver->flags = TTY_DRIVER_REAL_RAW;
|
1546 |
|
|
|
1547 |
|
|
tty_set_operations(hvcs_tty_driver, &hvcs_ops);
|
1548 |
|
|
|
1549 |
|
|
/*
|
1550 |
|
|
* The following call will result in sysfs entries that denote the
|
1551 |
|
|
* dynamically assigned major and minor numbers for our devices.
|
1552 |
|
|
*/
|
1553 |
|
|
if (tty_register_driver(hvcs_tty_driver)) {
|
1554 |
|
|
printk(KERN_ERR "HVCS: registration as a tty driver failed.\n");
|
1555 |
|
|
rc = -EIO;
|
1556 |
|
|
goto register_fail;
|
1557 |
|
|
}
|
1558 |
|
|
|
1559 |
|
|
hvcs_pi_buff = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
1560 |
|
|
if (!hvcs_pi_buff) {
|
1561 |
|
|
rc = -ENOMEM;
|
1562 |
|
|
goto buff_alloc_fail;
|
1563 |
|
|
}
|
1564 |
|
|
|
1565 |
|
|
hvcs_task = kthread_run(khvcsd, NULL, "khvcsd");
|
1566 |
|
|
if (IS_ERR(hvcs_task)) {
|
1567 |
|
|
printk(KERN_ERR "HVCS: khvcsd creation failed. Driver not loaded.\n");
|
1568 |
|
|
rc = -EIO;
|
1569 |
|
|
goto kthread_fail;
|
1570 |
|
|
}
|
1571 |
|
|
|
1572 |
|
|
rc = vio_register_driver(&hvcs_vio_driver);
|
1573 |
|
|
if (rc) {
|
1574 |
|
|
printk(KERN_ERR "HVCS: can't register vio driver\n");
|
1575 |
|
|
goto vio_fail;
|
1576 |
|
|
}
|
1577 |
|
|
|
1578 |
|
|
/*
|
1579 |
|
|
* This needs to be done AFTER the vio_register_driver() call or else
|
1580 |
|
|
* the kobjects won't be initialized properly.
|
1581 |
|
|
*/
|
1582 |
|
|
rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan);
|
1583 |
|
|
if (rc) {
|
1584 |
|
|
printk(KERN_ERR "HVCS: sysfs attr create failed\n");
|
1585 |
|
|
goto attr_fail;
|
1586 |
|
|
}
|
1587 |
|
|
|
1588 |
|
|
printk(KERN_INFO "HVCS: driver module inserted.\n");
|
1589 |
|
|
|
1590 |
|
|
return 0;
|
1591 |
|
|
|
1592 |
|
|
attr_fail:
|
1593 |
|
|
vio_unregister_driver(&hvcs_vio_driver);
|
1594 |
|
|
vio_fail:
|
1595 |
|
|
kthread_stop(hvcs_task);
|
1596 |
|
|
kthread_fail:
|
1597 |
|
|
kfree(hvcs_pi_buff);
|
1598 |
|
|
buff_alloc_fail:
|
1599 |
|
|
tty_unregister_driver(hvcs_tty_driver);
|
1600 |
|
|
register_fail:
|
1601 |
|
|
hvcs_free_index_list();
|
1602 |
|
|
index_fail:
|
1603 |
|
|
put_tty_driver(hvcs_tty_driver);
|
1604 |
|
|
hvcs_tty_driver = NULL;
|
1605 |
|
|
return rc;
|
1606 |
|
|
}
|
1607 |
|
|
|
1608 |
|
|
static void __exit hvcs_module_exit(void)
|
1609 |
|
|
{
|
1610 |
|
|
/*
|
1611 |
|
|
* This driver receives hvcs_remove callbacks for each device upon
|
1612 |
|
|
* module removal.
|
1613 |
|
|
*/
|
1614 |
|
|
|
1615 |
|
|
/*
|
1616 |
|
|
* This synchronous operation will wake the khvcsd kthread if it is
|
1617 |
|
|
* asleep and will return when khvcsd has terminated.
|
1618 |
|
|
*/
|
1619 |
|
|
kthread_stop(hvcs_task);
|
1620 |
|
|
|
1621 |
|
|
spin_lock(&hvcs_pi_lock);
|
1622 |
|
|
kfree(hvcs_pi_buff);
|
1623 |
|
|
hvcs_pi_buff = NULL;
|
1624 |
|
|
spin_unlock(&hvcs_pi_lock);
|
1625 |
|
|
|
1626 |
|
|
driver_remove_file(&hvcs_vio_driver.driver, &driver_attr_rescan);
|
1627 |
|
|
|
1628 |
|
|
vio_unregister_driver(&hvcs_vio_driver);
|
1629 |
|
|
|
1630 |
|
|
tty_unregister_driver(hvcs_tty_driver);
|
1631 |
|
|
|
1632 |
|
|
hvcs_free_index_list();
|
1633 |
|
|
|
1634 |
|
|
put_tty_driver(hvcs_tty_driver);
|
1635 |
|
|
|
1636 |
|
|
printk(KERN_INFO "HVCS: driver module removed.\n");
|
1637 |
|
|
}
|
1638 |
|
|
|
1639 |
|
|
module_init(hvcs_module_init);
|
1640 |
|
|
module_exit(hvcs_module_exit);
|