Update DEP-5 uri
[debian/omnibook.git] / pio.c
1 /*
2  * pio.c -- low level functions I/O ports
3  * 
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2, or (at your option) any
7  * later version.
8  * 
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * Written by Soós Péter <sp@osb.hu>, 2002-2004
15  * Modified by Mathieu Bérard <mathieu.berard@crans.org>, 2006
16  */
17
18 #include "omnibook.h"
19
20 #include <linux/types.h>
21 #include <linux/delay.h>
22 #include <linux/sched.h>
23 #include <linux/spinlock.h>
24 #include <linux/ioport.h>
25
26 #include <asm/io.h>
27 #include "hardware.h"
28
29 /*
30  * IO port backend. Only support single or dual ports operations
31  * private data structure: it's the linked list of requested ports
32  * 
33  * Race condition issue: omnibook_pio_init/exit functions are only called from
34  * omnibook_backend_match and omnibook_remove from init.c, this should happen
35  * only at module init/exit time so there is no need for a lock.
36  */
37
38 struct pio_priv_data_t {
39         unsigned long addr;
40         struct kref refcount;
41         struct list_head list;
42 };
43
44 static struct pio_priv_data_t pio_priv_data = {
45         .addr = 0,
46         .list = LIST_HEAD_INIT(pio_priv_data.list),
47 };
48
49 /*
50  * Match an entry in the linked list helper function: see if we have and entry
51  * whose addr field match maddr
52  */
53 static struct pio_priv_data_t *omnibook_match_port(struct pio_priv_data_t *data,
54                                                       unsigned long maddr)
55 {
56         struct pio_priv_data_t *cursor;
57
58         list_for_each_entry(cursor, &data->list, list) {
59                 if (cursor->addr == maddr) {
60                         return cursor;
61                 }
62         }
63         return NULL;
64 }
65
66 /*
67  * See if we have to request raddr
68  */
69 static int omnibook_claim_port(struct pio_priv_data_t *data, unsigned long raddr)
70 {
71         struct pio_priv_data_t *match, *new;
72
73         match = omnibook_match_port(data, raddr);
74         if (match) {
75                 /* Already requested by us: increment kref and quit */
76                 kref_get(&match->refcount);
77                 return 0;
78         }
79
80         /* there was no match: request the region and add to list */
81         if (!request_region(raddr, 1, OMNIBOOK_MODULE_NAME)) {
82                 printk(O_ERR "Request I/O port error\n");
83                 return -ENODEV;
84         }
85
86         new = kmalloc(sizeof(struct pio_priv_data_t), GFP_KERNEL);
87         if (!new) {
88                 release_region(raddr, 1);
89                 return -ENOMEM;
90         }
91
92         kref_init(&new->refcount);
93         new->addr = raddr;
94         list_add(&new->list, &data->list);
95
96         return 0;
97 }
98
99 /*
100  * Register read_addr and write_addr
101  */
102 static int omnibook_pio_init(const struct omnibook_operation *io_op)
103 {
104         int retval = 0;
105
106         if (io_op->read_addr
107             && (retval = omnibook_claim_port(io_op->backend->data, io_op->read_addr)))
108                 goto out;
109
110         if (io_op->write_addr && (io_op->write_addr != io_op->read_addr))
111                 retval = omnibook_claim_port(io_op->backend->data, io_op->write_addr);
112
113       out:
114         return retval;
115 }
116
117 /*
118  * REALLY release a port
119  */
120 static void omnibook_free_port(struct kref *ref)
121 {
122         struct pio_priv_data_t *data;
123
124         data = container_of(ref, struct pio_priv_data_t, refcount);
125         release_region(data->addr, 1);
126         list_del(&data->list);
127         kfree(data);
128 }
129
130 /*
131  * Unregister read_addr and write_addr
132  */
133 static void omnibook_pio_exit(const struct omnibook_operation *io_op)
134 {
135         struct pio_priv_data_t *match;
136
137         match = omnibook_match_port(io_op->backend->data, io_op->read_addr);
138         if (match)
139                 kref_put(&match->refcount, omnibook_free_port);
140
141         match = omnibook_match_port(io_op->backend->data, io_op->write_addr);
142         if (match)
143                 kref_put(&match->refcount, omnibook_free_port);
144
145 }
146
147 static int omnibook_io_read(const struct omnibook_operation *io_op, u8 * value)
148 {
149         *value = inb(io_op->read_addr);
150         if (io_op->read_mask)
151                 *value &= io_op->read_mask;
152         return 0;
153 }
154
155 static int omnibook_io_write(const struct omnibook_operation *io_op, u8 value)
156 {
157         outb(io_op->write_addr, value);
158         return 0;
159 }
160
161 /*
162  * Backend interface declarations
163  */
164 struct omnibook_backend pio_backend = {
165         .name = "pio",
166         .data = &pio_priv_data,
167         .init = omnibook_pio_init,
168         .exit = omnibook_pio_exit,
169         .byte_read = omnibook_io_read,
170         .byte_write = omnibook_io_write,
171 };
172
173 /* End of file */