7941c5813ebafdcfb7faab944ec3d01043721e02
[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  *
28  */
29
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/systm.h>
33 #include <sys/device.h>
34 #include <sys/module.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/uio.h>
38 #include <sys/fcntl.h>
39
40 #include "smbconf.h"
41 #include "smbus.h"
42 #include "smb.h"
43
44 #include "smbus_if.h"
45
46 #define BUFSIZE 1024
47
48 struct smb_softc {
49
50         int sc_count;                   /* >0 if device opened */
51         cdev_t sc_devnode;
52 };
53
54 #define IIC_SOFTC(unit) \
55         ((struct smb_softc *)devclass_get_softc(smb_devclass, (unit)))
56
57 #define IIC_DEVICE(unit) \
58         (devclass_get_device(smb_devclass, (unit)))
59
60 static void smb_identify(driver_t *driver, device_t parent);
61 static int smb_probe(device_t);
62 static int smb_attach(device_t);
63 static int smb_detach(device_t);
64
65 static devclass_t smb_devclass;
66
67 static device_method_t smb_methods[] = {
68         /* device interface */
69         DEVMETHOD(device_identify,      smb_identify),
70         DEVMETHOD(device_probe,         smb_probe),
71         DEVMETHOD(device_attach,        smb_attach),
72         DEVMETHOD(device_detach,        smb_detach),
73
74         /* smbus interface */
75         DEVMETHOD(smbus_intr,           smbus_generic_intr),
76
77         { 0, 0 }
78 };
79
80 static driver_t smb_driver = {
81         "smb",
82         smb_methods,
83         sizeof(struct smb_softc),
84 };
85
86 static  d_open_t        smbopen;
87 static  d_close_t       smbclose;
88 static  d_ioctl_t       smbioctl;
89
90 static struct dev_ops smb_ops = {
91         { "smb", 0, 0 },
92         .d_open =       smbopen,
93         .d_close =      smbclose,
94         .d_ioctl =      smbioctl,
95 };
96
97 static void
98 smb_identify(driver_t *driver, device_t parent)
99 {
100         if (device_find_child(parent, "smb", -1) == NULL)
101                 BUS_ADD_CHILD(parent, parent, 0, "smb", -1);
102 }
103
104 static int
105 smb_probe(device_t dev)
106 {
107         device_set_desc(dev, "SMBus generic I/O");
108
109         return (0);
110 }
111
112 static int
113 smb_attach(device_t dev)
114 {
115         struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
116
117         if (!sc)
118                 return (ENOMEM);
119
120         bzero(sc, sizeof(struct smb_softc *));
121
122         sc->sc_devnode = make_dev(&smb_ops, device_get_unit(dev),
123                         UID_ROOT, GID_WHEEL,
124                         0600, "smb%d", device_get_unit(dev));
125
126         return (0);
127 }
128
129 static int
130 smb_detach(device_t dev)
131 {
132         struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
133
134         if (sc->sc_devnode)
135                 dev_ops_remove_minor(&smb_ops, device_get_unit(dev));
136
137         return (0);
138 }
139
140 static int
141 smbopen (struct dev_open_args *ap)
142 {
143         cdev_t dev = ap->a_head.a_dev;
144         struct smb_softc *sc = IIC_SOFTC(minor(dev));
145
146         if (sc == NULL)
147                 return (ENXIO);
148
149         if (sc->sc_count != 0)
150                 return (EBUSY);
151
152         sc->sc_count++;
153
154         return (0);
155 }
156
157 static int
158 smbclose(struct dev_close_args *ap)
159 {
160         cdev_t dev = ap->a_head.a_dev;
161         struct smb_softc *sc = IIC_SOFTC(minor(dev));
162
163         if (sc == NULL)
164                 return (ENXIO);
165
166         if (sc->sc_count == 0)
167                 /* This is not supposed to happen. */
168                 return (0);
169
170         sc->sc_count--;
171
172         return (0);
173 }
174
175 #if 0
176 static int
177 smbwrite(struct dev_write_args *ap)
178 {
179         return (EINVAL);
180 }
181
182 static int
183 smbread(struct dev_read_args *ap)
184 {
185         return (EINVAL);
186 }
187 #endif
188
189 static int
190 smbioctl(struct dev_ioctl_args *ap)
191 {
192         cdev_t dev = ap->a_head.a_dev;
193         char buf[SMB_MAXBLOCKSIZE];
194         device_t parent;
195         struct smbcmd *s = (struct smbcmd *)ap->a_data;
196         struct smb_softc *sc = IIC_SOFTC(minor(dev));
197         device_t smbdev = IIC_DEVICE(minor(dev));
198         int error;
199         short w;
200         u_char count;
201         char c;
202
203         if (sc == NULL)
204                 return (ENXIO);
205         if (s == NULL)
206                 return (EINVAL);
207
208         parent = device_get_parent(smbdev);
209
210         /* Allocate the bus. */
211         if ((error = smbus_request_bus(parent, smbdev,
212                         (ap->a_fflag & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
213                 return (error);
214
215         switch (ap->a_cmd) {
216         case SMB_QUICK_WRITE:
217                 error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE));
218                 break;
219
220         case SMB_QUICK_READ:
221                 error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD));
222                 break;
223
224         case SMB_SENDB:
225                 error = smbus_error(smbus_sendb(parent, s->slave, s->cmd));
226                 break;
227
228         case SMB_RECVB:
229                 error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd));
230                 break;
231
232         case SMB_WRITEB:
233                 error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
234                                                 s->data.byte));
235                 break;
236
237         case SMB_WRITEW:
238                 error = smbus_error(smbus_writew(parent, s->slave,
239                                                 s->cmd, s->data.word));
240                 break;
241
242         case SMB_READB:
243                 if (s->data.byte_ptr) {
244                         error = smbus_error(smbus_readb(parent, s->slave,
245                                                 s->cmd, &c));
246                         if (error)
247                                 break;
248                         error = copyout(&c, s->data.byte_ptr,
249                                         sizeof(*(s->data.byte_ptr)));
250                 }
251                 break;
252
253         case SMB_READW:
254                 if (s->data.word_ptr) {
255                         error = smbus_error(smbus_readw(parent, s->slave,
256                                                 s->cmd, &w));
257                         if (error == 0) {
258                                 error = copyout(&w, s->data.word_ptr,
259                                                 sizeof(*(s->data.word_ptr)));
260                         }
261                 }
262                 break;
263
264         case SMB_PCALL:
265                 if (s->data.process.rdata) {
266                         error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
267                                 s->data.process.sdata, &w));
268                         if (error)
269                                 break;
270                         error = copyout(&w, s->data.process.rdata,
271                                         sizeof(*(s->data.process.rdata)));
272                 }
273
274                 break;
275
276         case SMB_BWRITE:
277                 if (s->count && s->data.byte_ptr) {
278                         if (s->count > SMB_MAXBLOCKSIZE)
279                                 s->count = SMB_MAXBLOCKSIZE;
280                         error = copyin(s->data.byte_ptr, buf, s->count);
281                         if (error)
282                                 break;
283                         error = smbus_error(smbus_bwrite(parent, s->slave,
284                                                 s->cmd, s->count, buf));
285                 }
286                 break;
287
288         case SMB_OLD_BREAD:
289         case SMB_BREAD:
290                 if (s->count && s->data.byte_ptr) {
291                         count = min(s->count, SMB_MAXBLOCKSIZE);
292                         error = smbus_error(smbus_bread(parent, s->slave,
293                                                 s->cmd, &count, buf));
294                         if (error)
295                                 break;
296                         error = copyout(buf, s->data.byte_ptr,
297                             min(count, s->count));
298                         s->count = count;
299                 }
300                 break;
301
302         default:
303                 error = ENOTTY;
304         }
305
306         smbus_release_bus(parent, smbdev);
307
308         return (error);
309 }
310
311 DRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, NULL, NULL);
312 MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
313 MODULE_VERSION(smb, 1);