#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 DECLARE_MUTEX(raw3270_sem);
/* List of 3270 devices. */
static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices);
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);
{
static const unsigned char wbuf[] =
{ 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 };
- unsigned long flags;
struct raw3270_ua *uap;
unsigned short count;
int 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();
* Add device to list and find the smallest unused minor
* number for it.
*/
- spin_lock(&raw3270_lock);
+ down(&raw3270_sem);
/* Keep the list sorted. */
minor = 0;
rp->minor = -1;
rp->minor = minor;
list_add_tail(&rp->list, &raw3270_devices);
}
- spin_unlock(&raw3270_lock);
+ up(&raw3270_sem);
/* No free minor number? Then give up. */
if (rp->minor == -1)
return -EUSERS;
raw3270_reset_device(rp);
raw3270_size_device(rp);
raw3270_reset_device(rp);
+ 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)) {
+ if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
list_for_each_entry(view, &rp->view_list, list)
if (view->fn->activate(view) == 0) {
rp->view = view;
struct raw3270 *rp;
int rc;
- spin_lock(&raw3270_lock);
+ down(&raw3270_sem);
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;
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
break;
}
- spin_unlock(&raw3270_lock);
+ up(&raw3270_sem);
return rc;
}
struct raw3270_view *view, *tmp;
unsigned long flags;
- spin_lock(&raw3270_lock);
+ down(&raw3270_sem);
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);
+ up(&raw3270_sem);
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(view) == 0) {
+ rp->view = nv;
break;
}
+ }
}
spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
/* Wait for reference counter to drop to zero. */
- atomic_dec(&view->ref_count);
+ atomic_sub(2, &view->ref_count);
wait_event(raw3270_wait_queue, atomic_read(&view->ref_count) == 0);
if (view->fn->free)
view->fn->free(view);
struct ccw_device *cdev;
/* Remove from device chain. */
- spin_lock(&raw3270_lock);
+ down(&raw3270_sem);
list_del_init(&rp->list);
- spin_unlock(&raw3270_lock);
+ up(&raw3270_sem);
/* Disconnect from ccw_device. */
cdev = rp->cdev;
sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group);
}
-/* Hackish. A notifier chain would be cleaner. */
-extern void tty3270_notifier(int index, int active);
+/*
+ * 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;
+ down(&raw3270_sem);
+ list_add_tail(&np->list, &raw3270_notifier);
+ list_for_each_entry(rp, &raw3270_devices, list) {
+ get_device(&rp->cdev->dev);
+ notifier(rp->minor, 1);
+ }
+ up(&raw3270_sem);
+ return 0;
+}
+
+void raw3270_unregister_notifier(void (*notifier)(int, int))
+{
+ struct raw3270_notifier *np;
+
+ down(&raw3270_sem);
+ list_for_each_entry(np, &raw3270_notifier, list)
+ if (np->notifier == notifier) {
+ list_del(&np->list);
+ kfree(np);
+ break;
+ }
+ up(&raw3270_sem);
+}
/*
* Set 3270 device online.
raw3270_set_online (struct ccw_device *cdev)
{
struct raw3270 *rp;
+ struct raw3270_notifier *np;
rp = raw3270_create_device(cdev);
if (IS_ERR(rp))
raw3270_size_device(rp);
raw3270_reset_device(rp);
raw3270_create_attributes(rp);
- tty3270_notifier(rp->minor, 1);
+ set_bit(RAW3270_FLAGS_READY, &rp->flags);
+ down(&raw3270_sem);
+ list_for_each_entry(np, &raw3270_notifier, list)
+ np->notifier(rp->minor, 1);
+ up(&raw3270_sem);
return 0;
}
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);
+ 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);
+ down(&raw3270_sem);
+ list_for_each_entry(np, &raw3270_notifier, list)
+ np->notifier(rp->minor, 0);
+ up(&raw3270_sem);
/* 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);
+ down(&raw3270_sem);
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);
+ up(&raw3270_sem);
}
return rc;
}
-void
+static void
raw3270_exit(void)
{
ccw_driver_unregister(&raw3270_ccw_driver);
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_deactivate_view);
EXPORT_SYMBOL(raw3270_start);
EXPORT_SYMBOL(raw3270_start_irq);
+EXPORT_SYMBOL(raw3270_register_notifier);
+EXPORT_SYMBOL(raw3270_unregister_notifier);
+EXPORT_SYMBOL(raw3270_wait_queue);