#include "raw3270.h"
+#include <linux/major.h>
+#include <linux/kdev_t.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+
+struct class *class3270;
+
/* The main 3270 data structure. */
struct raw3270 {
struct list_head list;
struct timer_list timer; /* Device timer. */
unsigned char *ascebc; /* ascii -> ebcdic table */
+ struct class_device *clttydev; /* 3270-class tty device ptr */
+ struct class_device *cltubdev; /* 3270-class tub device ptr */
};
/* raw3270->flags */
#define RAW3270_FLAGS_14BITADDR 0 /* 14-bit buffer addresses */
#define RAW3270_FLAGS_BUSY 1 /* Device busy, leave it alone */
#define RAW3270_FLAGS_ATTN 2 /* Device sent an ATTN interrupt */
-#define RAW3270_FLAGS_SHUTDOWN 4 /* Device is in offline processing */
+#define RAW3270_FLAGS_READY 4 /* Device is useable by views */
#define RAW3270_FLAGS_CONSOLE 8 /* Device is the console. */
-/* Lock to protect global data of raw3270 (devices, views, etc). */
-static spinlock_t raw3270_lock = SPIN_LOCK_UNLOCKED;
+/* Semaphore to protect global data of raw3270 (devices, views, etc). */
+static DEFINE_MUTEX(raw3270_mutex);
/* List of 3270 devices. */
static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices);
/* Module parameters */
static int tubxcorrect = 0;
-MODULE_PARM(tubxcorrect, "i");
+module_param(tubxcorrect, bool, 0);
/*
* Wait queue for device init/delete, view delete.
struct raw3270_request *rq;
/* Allocate request structure */
- rq = kmalloc(sizeof(struct raw3270_request), GFP_KERNEL | GFP_DMA);
+ rq = kzalloc(sizeof(struct raw3270_request), GFP_KERNEL | GFP_DMA);
if (!rq)
return ERR_PTR(-ENOMEM);
- memset(rq, 0, sizeof(struct raw3270_request));
/* alloc output buffer. */
if (size > 0) {
void
raw3270_request_free (struct raw3270_request *rq)
{
- if (rq->buffer)
- kfree(rq->buffer);
+ kfree(rq->buffer);
kfree(rq);
}
rp = view->dev;
if (!rp || rp->view != view)
rc = -EACCES;
- else if (test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags))
+ else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
rc = -ENODEV;
else
rc = __raw3270_start(rp, view, rq);
return rc;
}
+int
+raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq)
+{
+ struct raw3270 *rp;
+ int rc;
+
+ rp = view->dev;
+ if (!rp || rp->view != view)
+ rc = -EACCES;
+ else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
+ rc = -ENODEV;
+ else
+ rc = __raw3270_start(rp, view, rq);
+ return rc;
+}
+
int
raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq)
{
if (IS_ERR(irb))
rc = RAW3270_IO_RETRY;
- else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END |
- DEV_STAT_UNIT_EXCEP)) {
+ else if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) {
+ rq->rc = -EIO;
+ rc = RAW3270_IO_DONE;
+ } else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END |
+ DEV_STAT_UNIT_EXCEP)) {
/* Handle CE-DE-UE and subsequent UDE */
set_bit(RAW3270_FLAGS_BUSY, &rp->flags);
rc = RAW3270_IO_BUSY;
rc = wait_event_interruptible(wq, raw3270_request_final(rq));
if (rc == -ERESTARTSYS) { /* Interrupted by a signal. */
raw3270_halt_io(view->dev, rq);
+ /* No wait for the halt to complete. */
+ wait_event(wq, raw3270_request_final(rq));
return -ERESTARTSYS;
}
return rq->rc;
{
static const unsigned char wbuf[] =
{ 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 };
- unsigned long flags;
struct raw3270_ua *uap;
unsigned short count;
int rc;
raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data);
rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request);
- if (rc) {
+ if (rc)
/* Check error cases: -ERESTARTSYS, -EIO and -EOPNOTSUPP */
- if (rc == -EOPNOTSUPP && MACHINE_IS_VM)
- return __raw3270_size_device_vm(rp);
return rc;
- }
/* Wait for attention interrupt. */
#ifdef CONFIG_TN3270_CONSOLE
if (raw3270_registered == 0) {
+ unsigned long flags;
+
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
while (!test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags))
wait_cons_dev();
down(&raw3270_init_sem);
rp->view = &raw3270_init_view;
raw3270_init_view.dev = rp;
- rc = __raw3270_size_device(rp);
+ if (MACHINE_IS_VM)
+ rc = __raw3270_size_device_vm(rp);
+ else
+ rc = __raw3270_size_device(rp);
raw3270_init_view.dev = 0;
rp->view = 0;
up(&raw3270_init_sem);
rp->model = 4;
if (rp->rows == 27 && rp->cols == 132)
rp->model = 5;
+ } else {
+ /* Couldn't detect size. Use default model 2. */
+ rp->model = 2;
+ rp->rows = 24;
+ rp->cols = 80;
+ return 0;
}
return rc;
}
return rc;
}
+int
+raw3270_reset(struct raw3270_view *view)
+{
+ struct raw3270 *rp;
+ int rc;
+
+ rp = view->dev;
+ if (!rp || rp->view != view)
+ rc = -EACCES;
+ else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
+ rc = -ENODEV;
+ else
+ rc = raw3270_reset_device(view->dev);
+ return rc;
+}
+
/*
* Setup new 3270 device.
*/
/*
* Add device to list and find the smallest unused minor
- * number for it.
+ * number for it. Note: there is no device with minor 0,
+ * see special case for fs3270.c:fs3270_open().
*/
- spin_lock(&raw3270_lock);
+ mutex_lock(&raw3270_mutex);
/* Keep the list sorted. */
- minor = 0;
+ minor = RAW3270_FIRSTMINOR;
rp->minor = -1;
list_for_each(l, &raw3270_devices) {
tmp = list_entry(l, struct raw3270, list);
}
minor++;
}
- if (rp->minor == -1 && minor < RAW3270_MAXDEVS) {
+ if (rp->minor == -1 && minor < RAW3270_MAXDEVS + RAW3270_FIRSTMINOR) {
rp->minor = minor;
list_add_tail(&rp->list, &raw3270_devices);
}
- spin_unlock(&raw3270_lock);
+ mutex_unlock(&raw3270_mutex);
/* No free minor number? Then give up. */
if (rp->minor == -1)
return -EUSERS;
if (rc)
return ERR_PTR(rc);
set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags);
- raw3270_reset_device(rp);
- raw3270_size_device(rp);
- raw3270_reset_device(rp);
+ rc = raw3270_reset_device(rp);
+ if (rc)
+ return ERR_PTR(rc);
+ rc = raw3270_size_device(rp);
+ if (rc)
+ return ERR_PTR(rc);
+ rc = raw3270_reset_device(rp);
+ if (rc)
+ return ERR_PTR(rc);
+ set_bit(RAW3270_FLAGS_READY, &rp->flags);
return rp;
}
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
if (rp->view == view)
rc = 0;
- else if (test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags))
+ else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
rc = -ENODEV;
else {
oldview = 0;
list_del_init(&view->list);
list_add_tail(&view->list, &rp->view_list);
/* Try to activate another view. */
- if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
- list_for_each_entry(view, &rp->view_list, list)
- if (view->fn->activate(view) == 0) {
- rp->view = view;
+ if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
+ list_for_each_entry(view, &rp->view_list, list) {
+ rp->view = view;
+ if (view->fn->activate(view) == 0)
break;
- }
+ rp->view = 0;
+ }
}
}
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
struct raw3270 *rp;
int rc;
- spin_lock(&raw3270_lock);
+ if (minor <= 0)
+ return -ENODEV;
+ mutex_lock(&raw3270_mutex);
rc = -ENODEV;
list_for_each_entry(rp, &raw3270_devices, list) {
if (rp->minor != minor)
continue;
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
- if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
+ if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
atomic_set(&view->ref_count, 2);
view->dev = rp;
view->fn = fn;
view->cols = rp->cols;
view->ascebc = rp->ascebc;
spin_lock_init(&view->lock);
- list_add_tail(&view->list, &rp->view_list);
+ list_add(&view->list, &rp->view_list);
rc = 0;
}
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
break;
}
- spin_unlock(&raw3270_lock);
+ mutex_unlock(&raw3270_mutex);
return rc;
}
struct raw3270_view *view, *tmp;
unsigned long flags;
- spin_lock(&raw3270_lock);
+ mutex_lock(&raw3270_mutex);
view = ERR_PTR(-ENODEV);
list_for_each_entry(rp, &raw3270_devices, list) {
if (rp->minor != minor)
continue;
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
- if (!test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
+ if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
+ view = ERR_PTR(-ENOENT);
list_for_each_entry(tmp, &rp->view_list, list) {
if (tmp->fn == fn) {
raw3270_get_view(tmp);
view = tmp;
+ break;
}
}
- } else
- view = ERR_PTR(-ENOENT);
+ }
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
break;
}
- spin_unlock(&raw3270_lock);
+ mutex_unlock(&raw3270_mutex);
return view;
}
{
unsigned long flags;
struct raw3270 *rp;
+ struct raw3270_view *nv;
rp = view->dev;
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
rp->view = 0;
}
list_del_init(&view->list);
- if (!rp->view && !test_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags)) {
+ if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
/* Try to activate another view. */
- list_for_each_entry(view, &rp->view_list, list)
- if (view->fn->activate(view) == 0) {
- rp->view = view;
+ list_for_each_entry(nv, &rp->view_list, list) {
+ if (nv->fn->activate(nv) == 0) {
+ rp->view = nv;
break;
}
+ }
}
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
/* Wait for reference counter to drop to zero. */
struct ccw_device *cdev;
/* Remove from device chain. */
- spin_lock(&raw3270_lock);
+ mutex_lock(&raw3270_mutex);
+ if (rp->clttydev)
+ class_device_destroy(class3270,
+ MKDEV(IBM_TTY3270_MAJOR, rp->minor));
+ if (rp->cltubdev)
+ class_device_destroy(class3270,
+ MKDEV(IBM_FS3270_MAJOR, rp->minor));
list_del_init(&rp->list);
- spin_unlock(&raw3270_lock);
+ mutex_unlock(&raw3270_mutex);
/* Disconnect from ccw_device. */
cdev = rp->cdev;
* Additional attributes for a 3270 device
*/
static ssize_t
-raw3270_model_show(struct device *dev, char *buf)
+raw3270_model_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%i\n",
((struct raw3270 *) dev->driver_data)->model);
static DEVICE_ATTR(model, 0444, raw3270_model_show, 0);
static ssize_t
-raw3270_rows_show(struct device *dev, char *buf)
+raw3270_rows_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%i\n",
((struct raw3270 *) dev->driver_data)->rows);
static DEVICE_ATTR(rows, 0444, raw3270_rows_show, 0);
static ssize_t
-raw3270_columns_show(struct device *dev, char *buf)
+raw3270_columns_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%i\n",
((struct raw3270 *) dev->driver_data)->cols);
{
//FIXME: check return code
sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group);
+ rp->clttydev =
+ class_device_create(class3270, NULL,
+ MKDEV(IBM_TTY3270_MAJOR, rp->minor),
+ &rp->cdev->dev, "tty%s",
+ rp->cdev->dev.bus_id);
+ rp->cltubdev =
+ class_device_create(class3270, NULL,
+ MKDEV(IBM_FS3270_MAJOR, rp->minor),
+ &rp->cdev->dev, "tub%s",
+ rp->cdev->dev.bus_id);
+}
+
+/*
+ * Notifier for device addition/removal
+ */
+struct raw3270_notifier {
+ struct list_head list;
+ void (*notifier)(int, int);
+};
+
+static struct list_head raw3270_notifier = LIST_HEAD_INIT(raw3270_notifier);
+
+int raw3270_register_notifier(void (*notifier)(int, int))
+{
+ struct raw3270_notifier *np;
+ struct raw3270 *rp;
+
+ np = kmalloc(sizeof(struct raw3270_notifier), GFP_KERNEL);
+ if (!np)
+ return -ENOMEM;
+ np->notifier = notifier;
+ mutex_lock(&raw3270_mutex);
+ list_add_tail(&np->list, &raw3270_notifier);
+ list_for_each_entry(rp, &raw3270_devices, list) {
+ get_device(&rp->cdev->dev);
+ notifier(rp->minor, 1);
+ }
+ mutex_unlock(&raw3270_mutex);
+ return 0;
}
-/* Hackish. A notifier chain would be cleaner. */
-extern void tty3270_notifier(int index, int active);
+void raw3270_unregister_notifier(void (*notifier)(int, int))
+{
+ struct raw3270_notifier *np;
+
+ mutex_lock(&raw3270_mutex);
+ list_for_each_entry(np, &raw3270_notifier, list)
+ if (np->notifier == notifier) {
+ list_del(&np->list);
+ kfree(np);
+ break;
+ }
+ mutex_unlock(&raw3270_mutex);
+}
/*
* Set 3270 device online.
raw3270_set_online (struct ccw_device *cdev)
{
struct raw3270 *rp;
+ struct raw3270_notifier *np;
+ int rc;
rp = raw3270_create_device(cdev);
if (IS_ERR(rp))
return PTR_ERR(rp);
- raw3270_reset_device(rp);
- raw3270_size_device(rp);
- raw3270_reset_device(rp);
+ rc = raw3270_reset_device(rp);
+ if (rc)
+ goto failure;
+ rc = raw3270_size_device(rp);
+ if (rc)
+ goto failure;
+ rc = raw3270_reset_device(rp);
+ if (rc)
+ goto failure;
raw3270_create_attributes(rp);
- tty3270_notifier(rp->minor, 1);
+ set_bit(RAW3270_FLAGS_READY, &rp->flags);
+ mutex_lock(&raw3270_mutex);
+ list_for_each_entry(np, &raw3270_notifier, list)
+ np->notifier(rp->minor, 1);
+ mutex_unlock(&raw3270_mutex);
return 0;
+
+failure:
+ raw3270_delete_device(rp);
+ return rc;
}
/*
unsigned long flags;
struct raw3270 *rp;
struct raw3270_view *v;
+ struct raw3270_notifier *np;
rp = cdev->dev.driver_data;
- set_bit(RAW3270_FLAGS_SHUTDOWN, &rp->flags);
+ /*
+ * _remove is the opposite of _probe; it's probe that
+ * should set up rp. raw3270_remove gets entered for
+ * devices even if they haven't been varied online.
+ * Thus, rp may validly be NULL here.
+ */
+ if (rp == NULL)
+ return;
+ clear_bit(RAW3270_FLAGS_READY, &rp->flags);
sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group);
}
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
- tty3270_notifier(rp->minor, 0);
+ mutex_lock(&raw3270_mutex);
+ list_for_each_entry(np, &raw3270_notifier, list)
+ np->notifier(rp->minor, 0);
+ mutex_unlock(&raw3270_mutex);
/* Reset 3270 device. */
raw3270_reset_device(rp);
.set_offline = &raw3270_set_offline,
};
-int
+static int
raw3270_init(void)
{
struct raw3270 *rp;
rc = ccw_driver_register(&raw3270_ccw_driver);
if (rc == 0) {
/* Create attributes for early (= console) device. */
- spin_lock(&raw3270_lock);
+ mutex_lock(&raw3270_mutex);
+ class3270 = class_create(THIS_MODULE, "3270");
list_for_each_entry(rp, &raw3270_devices, list) {
get_device(&rp->cdev->dev);
raw3270_create_attributes(rp);
- tty3270_notifier(rp->minor, 1);
}
- spin_unlock(&raw3270_lock);
+ mutex_unlock(&raw3270_mutex);
}
return rc;
}
-void
+static void
raw3270_exit(void)
{
ccw_driver_unregister(&raw3270_ccw_driver);
+ class_destroy(class3270);
}
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(raw3270_init);
-EXPORT_SYMBOL(raw3270_exit);
+module_init(raw3270_init);
+module_exit(raw3270_exit);
+
EXPORT_SYMBOL(raw3270_request_alloc);
EXPORT_SYMBOL(raw3270_request_free);
EXPORT_SYMBOL(raw3270_request_reset);
EXPORT_SYMBOL(raw3270_activate_view);
EXPORT_SYMBOL(raw3270_deactivate_view);
EXPORT_SYMBOL(raw3270_start);
+EXPORT_SYMBOL(raw3270_start_locked);
EXPORT_SYMBOL(raw3270_start_irq);
+EXPORT_SYMBOL(raw3270_reset);
+EXPORT_SYMBOL(raw3270_register_notifier);
+EXPORT_SYMBOL(raw3270_unregister_notifier);
+EXPORT_SYMBOL(raw3270_wait_queue);