iicbus: Bring us closer to FreeBSD.
[dragonfly.git] / sys / bus / smbus / smb.c
1 /*-
2  * Copyright (c) 1998, 2001 Nicolas Souchu
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/smbus/smb.c,v 1.34.8.2 2006/09/22 19:19:16 jhb Exp $
27  * $DragonFly: src/sys/bus/smbus/smb.c,v 1.9 2006/09/10 01:26:33 dillon Exp $
28  *
29  */
30
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/systm.h>
34 #include <sys/device.h>
35 #include <sys/module.h>
36 #include <sys/bus.h>
37 #include <sys/conf.h>
38 #include <sys/uio.h>
39 #include <sys/fcntl.h>
40
41 #include "smbconf.h"
42 #include "smbus.h"
43 #include "smb.h"
44
45 #include "smbus_if.h"
46
47 #define BUFSIZE 1024
48
49 struct smb_softc {
50
51         int sc_count;                   /* >0 if device opened */
52         cdev_t sc_devnode;
53 };
54
55 #define IIC_SOFTC(unit) \
56         ((struct smb_softc *)devclass_get_softc(smb_devclass, (unit)))
57
58 #define IIC_DEVICE(unit) \
59         (devclass_get_device(smb_devclass, (unit)))
60
61 static void smb_identify(driver_t *driver, device_t parent);
62 static int smb_probe(device_t);
63 static int smb_attach(device_t);
64 static int smb_detach(device_t);
65
66 static devclass_t smb_devclass;
67
68 static device_method_t smb_methods[] = {
69         /* device interface */
70         DEVMETHOD(device_identify,      smb_identify),
71         DEVMETHOD(device_probe,         smb_probe),
72         DEVMETHOD(device_attach,        smb_attach),
73         DEVMETHOD(device_detach,        smb_detach),
74
75         /* smbus interface */
76         DEVMETHOD(smbus_intr,           smbus_generic_intr),
77
78         { 0, 0 }
79 };
80
81 static driver_t smb_driver = {
82         "smb",
83         smb_methods,
84         sizeof(struct smb_softc),
85 };
86
87 static  d_open_t        smbopen;
88 static  d_close_t       smbclose;
89 static  d_ioctl_t       smbioctl;
90
91 #define CDEV_MAJOR 106
92 static struct dev_ops smb_ops = {
93         { "smb", CDEV_MAJOR, 0 },
94         .d_open =       smbopen,
95         .d_close =      smbclose,
96         .d_ioctl =      smbioctl,
97 };
98
99 static void
100 smb_identify(driver_t *driver, device_t parent)
101 {
102         if (device_find_child(parent, "smb", -1) == NULL)
103                 BUS_ADD_CHILD(parent, parent, 0, "smb", -1);
104 }
105
106 static int
107 smb_probe(device_t dev)
108 {
109         device_set_desc(dev, "SMBus generic I/O");
110
111         return (0);
112 }
113
114 static int
115 smb_attach(device_t dev)
116 {
117         struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
118
119         if (!sc)
120                 return (ENOMEM);
121
122         bzero(sc, sizeof(struct smb_softc *));
123
124         sc->sc_devnode = make_dev(&smb_ops, device_get_unit(dev),
125                         UID_ROOT, GID_WHEEL,
126                         0600, "smb%d", device_get_unit(dev));
127
128         return (0);
129 }
130
131 static int
132 smb_detach(device_t dev)
133 {
134         struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
135
136         if (sc->sc_devnode)
137                 dev_ops_remove_minor(&smb_ops, device_get_unit(dev));
138
139         return (0);
140 }
141
142 static int
143 smbopen (struct dev_open_args *ap)
144 {
145         cdev_t dev = ap->a_head.a_dev;
146         struct smb_softc *sc = IIC_SOFTC(minor(dev));
147
148         if (sc == NULL)
149                 return (ENXIO);
150
151         if (sc->sc_count != 0)
152                 return (EBUSY);
153
154         sc->sc_count++;
155
156         return (0);
157 }
158
159 static int
160 smbclose(struct dev_close_args *ap)
161 {
162         cdev_t dev = ap->a_head.a_dev;
163         struct smb_softc *sc = IIC_SOFTC(minor(dev));
164
165         if (sc == NULL)
166                 return (ENXIO);
167
168         if (sc->sc_count == 0)
169                 /* This is not supposed to happen. */
170                 return (0);
171
172         sc->sc_count--;
173
174         return (0);
175 }
176
177 #if 0
178 static int
179 smbwrite(struct dev_write_args *ap)
180 {
181         return (EINVAL);
182 }
183
184 static int
185 smbread(struct dev_read_args *ap)
186 {
187         return (EINVAL);
188 }
189 #endif
190
191 static int
192 smbioctl(struct dev_ioctl_args *ap)
193 {
194         cdev_t dev = ap->a_head.a_dev;
195         char buf[SMB_MAXBLOCKSIZE];
196         device_t parent;
197         struct smbcmd *s = (struct smbcmd *)ap->a_data;
198         struct smb_softc *sc = IIC_SOFTC(minor(dev));
199         device_t smbdev = IIC_DEVICE(minor(dev));
200         int error;
201         short w;
202         u_char count;
203         char c;
204
205         if (sc == NULL)
206                 return (ENXIO);
207         if (s == NULL)
208                 return (EINVAL);
209
210         parent = device_get_parent(smbdev);
211
212         /* Allocate the bus. */
213         if ((error = smbus_request_bus(parent, smbdev,
214                         (ap->a_fflag & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
215                 return (error);
216
217         switch (ap->a_cmd) {
218         case SMB_QUICK_WRITE:
219                 error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE));
220                 break;
221
222         case SMB_QUICK_READ:
223                 error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD));
224                 break;
225
226         case SMB_SENDB:
227                 error = smbus_error(smbus_sendb(parent, s->slave, s->cmd));
228                 break;
229
230         case SMB_RECVB:
231                 error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd));
232                 break;
233
234         case SMB_WRITEB:
235                 error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
236                                                 s->data.byte));
237                 break;
238
239         case SMB_WRITEW:
240                 error = smbus_error(smbus_writew(parent, s->slave,
241                                                 s->cmd, s->data.word));
242                 break;
243
244         case SMB_READB:
245                 if (s->data.byte_ptr) {
246                         error = smbus_error(smbus_readb(parent, s->slave,
247                                                 s->cmd, &c));
248                         if (error)
249                                 break;
250                         error = copyout(&c, s->data.byte_ptr,
251                                         sizeof(*(s->data.byte_ptr)));
252                 }
253                 break;
254
255         case SMB_READW:
256                 if (s->data.word_ptr) {
257                         error = smbus_error(smbus_readw(parent, s->slave,
258                                                 s->cmd, &w));
259                         if (error == 0) {
260                                 error = copyout(&w, s->data.word_ptr,
261                                                 sizeof(*(s->data.word_ptr)));
262                         }
263                 }
264                 break;
265
266         case SMB_PCALL:
267                 if (s->data.process.rdata) {
268                         error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
269                                 s->data.process.sdata, &w));
270                         if (error)
271                                 break;
272                         error = copyout(&w, s->data.process.rdata,
273                                         sizeof(*(s->data.process.rdata)));
274                 }
275
276                 break;
277
278         case SMB_BWRITE:
279                 if (s->count && s->data.byte_ptr) {
280                         if (s->count > SMB_MAXBLOCKSIZE)
281                                 s->count = SMB_MAXBLOCKSIZE;
282                         error = copyin(s->data.byte_ptr, buf, s->count);
283                         if (error)
284                                 break;
285                         error = smbus_error(smbus_bwrite(parent, s->slave,
286                                                 s->cmd, s->count, buf));
287                 }
288                 break;
289
290         case SMB_OLD_BREAD:
291         case SMB_BREAD:
292                 if (s->count && s->data.byte_ptr) {
293                         count = min(s->count, SMB_MAXBLOCKSIZE);
294                         error = smbus_error(smbus_bread(parent, s->slave,
295                                                 s->cmd, &count, buf));
296                         if (error)
297                                 break;
298                         error = copyout(buf, s->data.byte_ptr,
299                             min(count, s->count));
300                         s->count = count;
301                 }
302                 break;
303
304         default:
305                 error = ENOTTY;
306         }
307
308         smbus_release_bus(parent, smbdev);
309
310         return (error);
311 }
312
313 DRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, 0, 0);
314 MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
315 MODULE_VERSION(smb, 1);