1*04a1c1a1SRobert Mustacchi /*
2*04a1c1a1SRobert Mustacchi * This file and its contents are supplied under the terms of the
3*04a1c1a1SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*04a1c1a1SRobert Mustacchi * You may only use this file in accordance with the terms of version
5*04a1c1a1SRobert Mustacchi * 1.0 of the CDDL.
6*04a1c1a1SRobert Mustacchi *
7*04a1c1a1SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*04a1c1a1SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*04a1c1a1SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*04a1c1a1SRobert Mustacchi */
11*04a1c1a1SRobert Mustacchi
12*04a1c1a1SRobert Mustacchi /*
13*04a1c1a1SRobert Mustacchi * Copyright 2025 Oxide Computer Company
14*04a1c1a1SRobert Mustacchi */
15*04a1c1a1SRobert Mustacchi
16*04a1c1a1SRobert Mustacchi /*
17*04a1c1a1SRobert Mustacchi * eedev(4D): EEPROM support module.
18*04a1c1a1SRobert Mustacchi *
19*04a1c1a1SRobert Mustacchi * This module exists to make it easier to read and write various eeprom style
20*04a1c1a1SRobert Mustacchi * devices and have a single implementation for the surrounding character glue.
21*04a1c1a1SRobert Mustacchi * It provides and exposes the minor nodes.
22*04a1c1a1SRobert Mustacchi *
23*04a1c1a1SRobert Mustacchi * --------------------------
24*04a1c1a1SRobert Mustacchi * Driver and User Interfaces
25*04a1c1a1SRobert Mustacchi * --------------------------
26*04a1c1a1SRobert Mustacchi *
27*04a1c1a1SRobert Mustacchi * Drivers can register a number of logical devices by creating an eedev_hdl_t
28*04a1c1a1SRobert Mustacchi * and registering it by calling eedev_create(). Once created, the eedev driver
29*04a1c1a1SRobert Mustacchi * creates a corresponding minor node which will show up under /dev/eeprom as
30*04a1c1a1SRobert Mustacchi * /dev/eeprom/<driver>/<instance>/<name>. When the driver doesn't provide a
31*04a1c1a1SRobert Mustacchi * name, "eeprom" is used. The way that this name is communicated to userland
32*04a1c1a1SRobert Mustacchi * and understood by the devfsadm plugin is to use a ':' delineated minor node
33*04a1c1a1SRobert Mustacchi * name. So when we create a node we use "<driver>:<instance>:<name>".
34*04a1c1a1SRobert Mustacchi *
35*04a1c1a1SRobert Mustacchi * As part of registering with us, the driver provides a bunch of information
36*04a1c1a1SRobert Mustacchi * about the device in question including:
37*04a1c1a1SRobert Mustacchi *
38*04a1c1a1SRobert Mustacchi * 1. The overall capacity of the device. We set the 64-bit DDI "Size" property
39*04a1c1a1SRobert Mustacchi * with this information. This is used by specfs in its VOP_GETATTR()
40*04a1c1a1SRobert Mustacchi * implementation allowing userland to see the size of the device.
41*04a1c1a1SRobert Mustacchi *
42*04a1c1a1SRobert Mustacchi * 2. The number of bytes per-device logical address. Consider a 512-byte
43*04a1c1a1SRobert Mustacchi * EEPROM. You can think of this generally as 512 1-byte registers. Some
44*04a1c1a1SRobert Mustacchi * devices may phrase this as 256 2-byte registers. This is not the same as
45*04a1c1a1SRobert Mustacchi * a device's page size. A different way to put it is that this is the
46*04a1c1a1SRobert Mustacchi * device's smallest read and write it can perform.
47*04a1c1a1SRobert Mustacchi *
48*04a1c1a1SRobert Mustacchi * 3. The device also gives us page segment information. This segmentation
49*04a1c1a1SRobert Mustacchi * information is used to make sure that I/O requests don't cross device
50*04a1c1a1SRobert Mustacchi * boundaries that would cause the device to read/write from the start of
51*04a1c1a1SRobert Mustacchi * the segment. For example, a device with a 32-byte page would can only
52*04a1c1a1SRobert Mustacchi * write bytes in a single 32-byte aligned region at a time. Exceeding this
53*04a1c1a1SRobert Mustacchi * leads it to continue writing at the start of the 32-byte region.
54*04a1c1a1SRobert Mustacchi * Something most folks don't want!
55*04a1c1a1SRobert Mustacchi *
56*04a1c1a1SRobert Mustacchi * 4. The device gives us information about the maximum amount of read and
57*04a1c1a1SRobert Mustacchi * write I/O it can do at any time. This may be a property of the device or
58*04a1c1a1SRobert Mustacchi * the property of the I/O bus that it's operating on. For example, an I2C
59*04a1c1a1SRobert Mustacchi * based EEPROM is going to be constrained by its controller. Some SMBus
60*04a1c1a1SRobert Mustacchi * controllers will limit the I/O to up to 32-bytes.
61*04a1c1a1SRobert Mustacchi *
62*04a1c1a1SRobert Mustacchi * When issuing a read() or write() request, the framework will inherently limit
63*04a1c1a1SRobert Mustacchi * the amount of I/O to be in accordance with this. In addition, today it always
64*04a1c1a1SRobert Mustacchi * returns short reads and short writes. This is that case where read(2) or
65*04a1c1a1SRobert Mustacchi * write(2) say they can return less data than was requested! We mostly do that
66*04a1c1a1SRobert Mustacchi * for simplicity at our end today.
67*04a1c1a1SRobert Mustacchi *
68*04a1c1a1SRobert Mustacchi * Finally, when it comes to device interfaces, we explicitly don't guarantee
69*04a1c1a1SRobert Mustacchi * any serialization to the device. We leave that at the discretion of the
70*04a1c1a1SRobert Mustacchi * device implementer.
71*04a1c1a1SRobert Mustacchi *
72*04a1c1a1SRobert Mustacchi * ----------------
73*04a1c1a1SRobert Mustacchi * Device Lifetimes
74*04a1c1a1SRobert Mustacchi * ----------------
75*04a1c1a1SRobert Mustacchi *
76*04a1c1a1SRobert Mustacchi * A side effect of the eedev pseudo-device owning the minor nodes is that it
77*04a1c1a1SRobert Mustacchi * means there is no way for us to correlate a call to detach() eedev(4D) with
78*04a1c1a1SRobert Mustacchi * that of a driver providing the EEPROM. Effectively, we end up implementing
79*04a1c1a1SRobert Mustacchi * the same logic as /devices. When a device detaches, we don't actually remove
80*04a1c1a1SRobert Mustacchi * the minor node. It is only when the driver is actually removed from the
81*04a1c1a1SRobert Mustacchi * system that we do.
82*04a1c1a1SRobert Mustacchi *
83*04a1c1a1SRobert Mustacchi * Instead, when someone calls open(2) on a device, we will ensure that the
84*04a1c1a1SRobert Mustacchi * provider module is loaded and that it has recreated its existing minor node.
85*04a1c1a1SRobert Mustacchi * Once that happens, as long as someone holds the eedev minor open we will have
86*04a1c1a1SRobert Mustacchi * a corresponding NDI hold on the device, ensuring it cannot disappear until
87*04a1c1a1SRobert Mustacchi * close(2) has been called. There is one bit of trickiness to be aware of: the
88*04a1c1a1SRobert Mustacchi * DDI will call open(2) multiple times, but it will only call close(2) at the
89*04a1c1a1SRobert Mustacchi * final time. In general, this is what we want, but it means that we don't
90*04a1c1a1SRobert Mustacchi * actually track the number of open(2) calls today because everything is using
91*04a1c1a1SRobert Mustacchi * the same minor. If we were to use cloning opens, then that would generally
92*04a1c1a1SRobert Mustacchi * change.
93*04a1c1a1SRobert Mustacchi *
94*04a1c1a1SRobert Mustacchi * This leads to the following overall locking rules:
95*04a1c1a1SRobert Mustacchi *
96*04a1c1a1SRobert Mustacchi * 1. Entering the NDI must be done while no other locks are held. It is
97*04a1c1a1SRobert Mustacchi * acceptable to put an NDI hold on a parent and then exit the NDI devi
98*04a1c1a1SRobert Mustacchi * lock.
99*04a1c1a1SRobert Mustacchi *
100*04a1c1a1SRobert Mustacchi * 2. No NDI operations should be performed while holding the eedev mutex. In
101*04a1c1a1SRobert Mustacchi * particular, alls to ndi_devi_config_one() (or others) should not be
102*04a1c1a1SRobert Mustacchi * performed while holding locks.
103*04a1c1a1SRobert Mustacchi *
104*04a1c1a1SRobert Mustacchi * 3. The eedev.eedev_mutex should be the first mutex taken in the driver and
105*04a1c1a1SRobert Mustacchi * used when looking at information about the overall state of the devices
106*04a1c1a1SRobert Mustacchi * and the corresponding list_t structures. Only one thread should attempt
107*04a1c1a1SRobert Mustacchi * to call into and bring a driver back to life.
108*04a1c1a1SRobert Mustacchi *
109*04a1c1a1SRobert Mustacchi * 4. When calling into devices to perform read() and write() operations, one
110*04a1c1a1SRobert Mustacchi * should not hold any locks. In general, only read-only information should
111*04a1c1a1SRobert Mustacchi * be required in those operations.
112*04a1c1a1SRobert Mustacchi *
113*04a1c1a1SRobert Mustacchi * -----------
114*04a1c1a1SRobert Mustacchi * Future Work
115*04a1c1a1SRobert Mustacchi * -----------
116*04a1c1a1SRobert Mustacchi *
117*04a1c1a1SRobert Mustacchi * There are a few areas and things that the eedev framework doesn't handle
118*04a1c1a1SRobert Mustacchi * today, that we think would be good for the future:
119*04a1c1a1SRobert Mustacchi *
120*04a1c1a1SRobert Mustacchi * 1. It would be nice to have support for FEXCL. We would implement this by
121*04a1c1a1SRobert Mustacchi * using a cloning open. If we do this, we should also add a corresponding
122*04a1c1a1SRobert Mustacchi * ioctl() to allow for similar behavior at non-open time that would go
123*04a1c1a1SRobert Mustacchi * alongside this like we have for other devices with transactions.
124*04a1c1a1SRobert Mustacchi *
125*04a1c1a1SRobert Mustacchi * 2. Today we don't plumb through any information about device security
126*04a1c1a1SRobert Mustacchi * features. Many devices support some form of write-protection. It would be
127*04a1c1a1SRobert Mustacchi * good to plumb this through and allow it be set from a series of ioctls
128*04a1c1a1SRobert Mustacchi * and to have a corresponding user command.
129*04a1c1a1SRobert Mustacchi *
130*04a1c1a1SRobert Mustacchi * 3. It may end up making sense to revisit the constraints that we have around
131*04a1c1a1SRobert Mustacchi * alignment and not performing read-modify-write if we have devices with a
132*04a1c1a1SRobert Mustacchi * multi-byte read/write granularity.
133*04a1c1a1SRobert Mustacchi *
134*04a1c1a1SRobert Mustacchi * 4. Similarly, based on experience from additional consumers, we may need to
135*04a1c1a1SRobert Mustacchi * revisit the fact that we don't try to perform I/O to completion. In
136*04a1c1a1SRobert Mustacchi * general, these devices are on the smaller end (< 1 MiB) and are not
137*04a1c1a1SRobert Mustacchi * designed assuming massive I/O, so this is hopefully not a problem.
138*04a1c1a1SRobert Mustacchi */
139*04a1c1a1SRobert Mustacchi
140*04a1c1a1SRobert Mustacchi #include <sys/ddi.h>
141*04a1c1a1SRobert Mustacchi #include <sys/sunddi.h>
142*04a1c1a1SRobert Mustacchi #include <sys/stat.h>
143*04a1c1a1SRobert Mustacchi #include <sys/open.h>
144*04a1c1a1SRobert Mustacchi #include <sys/types.h>
145*04a1c1a1SRobert Mustacchi #include <sys/file.h>
146*04a1c1a1SRobert Mustacchi #include <sys/conf.h>
147*04a1c1a1SRobert Mustacchi #include <sys/avl.h>
148*04a1c1a1SRobert Mustacchi #include <sys/stddef.h>
149*04a1c1a1SRobert Mustacchi #include <sys/sysmacros.h>
150*04a1c1a1SRobert Mustacchi #include <sys/id_space.h>
151*04a1c1a1SRobert Mustacchi #include <sys/mkdev.h>
152*04a1c1a1SRobert Mustacchi #include <sys/sunndi.h>
153*04a1c1a1SRobert Mustacchi #include <sys/esunddi.h>
154*04a1c1a1SRobert Mustacchi #include <sys/ctype.h>
155*04a1c1a1SRobert Mustacchi #include <sys/fs/dv_node.h>
156*04a1c1a1SRobert Mustacchi
157*04a1c1a1SRobert Mustacchi #include "eedev.h"
158*04a1c1a1SRobert Mustacchi
159*04a1c1a1SRobert Mustacchi /*
160*04a1c1a1SRobert Mustacchi * Minimum and maximum minors. These currently are designed to cover devices
161*04a1c1a1SRobert Mustacchi * which keep us in the range of [1, MAXMIN32]. 0 is reserved for a control
162*04a1c1a1SRobert Mustacchi * interface if it's ever required. If we end up with minors that cover user
163*04a1c1a1SRobert Mustacchi * state, then we should create a second range starting at MAXMIN32 + 1 and
164*04a1c1a1SRobert Mustacchi * covering a generous number of entries.
165*04a1c1a1SRobert Mustacchi */
166*04a1c1a1SRobert Mustacchi #define EEDEV_MINOR_MIN 1
167*04a1c1a1SRobert Mustacchi #define EEDEV_MINOR_MAX MAXMIN32
168*04a1c1a1SRobert Mustacchi
169*04a1c1a1SRobert Mustacchi typedef struct eedev {
170*04a1c1a1SRobert Mustacchi kmutex_t eedev_mutex;
171*04a1c1a1SRobert Mustacchi list_t eedev_list;
172*04a1c1a1SRobert Mustacchi list_t eedev_dips;
173*04a1c1a1SRobert Mustacchi id_space_t *eedev_idspace;
174*04a1c1a1SRobert Mustacchi dev_info_t *eedev_dip;
175*04a1c1a1SRobert Mustacchi } eedev_t;
176*04a1c1a1SRobert Mustacchi
177*04a1c1a1SRobert Mustacchi static eedev_t eedev;
178*04a1c1a1SRobert Mustacchi
179*04a1c1a1SRobert Mustacchi typedef enum {
180*04a1c1a1SRobert Mustacchi /*
181*04a1c1a1SRobert Mustacchi * Indicates that the device should be treated as read-only.
182*04a1c1a1SRobert Mustacchi */
183*04a1c1a1SRobert Mustacchi EEDEV_F_READ_ONLY = 1 << 0,
184*04a1c1a1SRobert Mustacchi /*
185*04a1c1a1SRobert Mustacchi * Indicates that the handle has allocated an id_t for a minor node.
186*04a1c1a1SRobert Mustacchi */
187*04a1c1a1SRobert Mustacchi EEDEV_F_ID_ALLOC = 1 << 1,
188*04a1c1a1SRobert Mustacchi /*
189*04a1c1a1SRobert Mustacchi * Indicates that the appropriate properties have been set on the minor.
190*04a1c1a1SRobert Mustacchi */
191*04a1c1a1SRobert Mustacchi EEDEV_F_MINOR_PROPS = 1 << 2,
192*04a1c1a1SRobert Mustacchi /*
193*04a1c1a1SRobert Mustacchi * Indicates that the actual minor node has been created.
194*04a1c1a1SRobert Mustacchi */
195*04a1c1a1SRobert Mustacchi EEDEV_F_MINOR_VALID = 1 << 3,
196*04a1c1a1SRobert Mustacchi /*
197*04a1c1a1SRobert Mustacchi * Indicates that someone is trying to actively check / validate that
198*04a1c1a1SRobert Mustacchi * this sensor is usable.
199*04a1c1a1SRobert Mustacchi */
200*04a1c1a1SRobert Mustacchi EEDEV_F_BUSY = 1 << 4,
201*04a1c1a1SRobert Mustacchi /*
202*04a1c1a1SRobert Mustacchi * This indicates that the eeprom driver is currently usable.
203*04a1c1a1SRobert Mustacchi * Effectively that no one has called detach on the provider driver yet.
204*04a1c1a1SRobert Mustacchi */
205*04a1c1a1SRobert Mustacchi EEDEV_F_USABLE = 1 << 5,
206*04a1c1a1SRobert Mustacchi /*
207*04a1c1a1SRobert Mustacchi * Indicates that this eeprom has a hold on its dev_info_t. This is set
208*04a1c1a1SRobert Mustacchi * between an open() and close(). Only a single open can set this.
209*04a1c1a1SRobert Mustacchi */
210*04a1c1a1SRobert Mustacchi EEDEV_F_HELD = 1 << 6
211*04a1c1a1SRobert Mustacchi } eedev_flags_t;
212*04a1c1a1SRobert Mustacchi
213*04a1c1a1SRobert Mustacchi typedef enum {
214*04a1c1a1SRobert Mustacchi EEDEV_DIP_F_REMOVED = 1 << 0
215*04a1c1a1SRobert Mustacchi } eedev_dip_flags_t;
216*04a1c1a1SRobert Mustacchi
217*04a1c1a1SRobert Mustacchi typedef struct eedev_dip {
218*04a1c1a1SRobert Mustacchi list_node_t ed_link;
219*04a1c1a1SRobert Mustacchi dev_info_t *ed_dip;
220*04a1c1a1SRobert Mustacchi char *ed_ua;
221*04a1c1a1SRobert Mustacchi eedev_dip_flags_t ed_flags;
222*04a1c1a1SRobert Mustacchi ddi_unbind_callback_t ed_cb;
223*04a1c1a1SRobert Mustacchi list_t ed_devs;
224*04a1c1a1SRobert Mustacchi } eedev_dip_t;
225*04a1c1a1SRobert Mustacchi
226*04a1c1a1SRobert Mustacchi struct eedev_hdl {
227*04a1c1a1SRobert Mustacchi list_node_t eh_link;
228*04a1c1a1SRobert Mustacchi list_node_t eh_dip_link;
229*04a1c1a1SRobert Mustacchi eedev_dip_t *eh_dip;
230*04a1c1a1SRobert Mustacchi kcondvar_t eh_cv;
231*04a1c1a1SRobert Mustacchi void *eh_driver;
232*04a1c1a1SRobert Mustacchi char *eh_name;
233*04a1c1a1SRobert Mustacchi const eedev_ops_t *eh_ops;
234*04a1c1a1SRobert Mustacchi id_t eh_minor;
235*04a1c1a1SRobert Mustacchi dev_t eh_dev;
236*04a1c1a1SRobert Mustacchi uint32_t eh_size;
237*04a1c1a1SRobert Mustacchi uint32_t eh_seg;
238*04a1c1a1SRobert Mustacchi uint32_t eh_read_gran;
239*04a1c1a1SRobert Mustacchi uint32_t eh_write_gran;
240*04a1c1a1SRobert Mustacchi uint32_t eh_max_read;
241*04a1c1a1SRobert Mustacchi uint32_t eh_max_write;
242*04a1c1a1SRobert Mustacchi eedev_flags_t eh_flags;
243*04a1c1a1SRobert Mustacchi uint32_t eh_nwaiters;
244*04a1c1a1SRobert Mustacchi };
245*04a1c1a1SRobert Mustacchi
246*04a1c1a1SRobert Mustacchi /*
247*04a1c1a1SRobert Mustacchi * A token number of maximum bytes to read/write in one go to a device if it
248*04a1c1a1SRobert Mustacchi * doesn't give us something more specific. This number was mostly a guess based
249*04a1c1a1SRobert Mustacchi * on common I2C device sizes and the resulting bus utilization time they
250*04a1c1a1SRobert Mustacchi * implied.
251*04a1c1a1SRobert Mustacchi *
252*04a1c1a1SRobert Mustacchi * This value will want to be revisited if SPI devices use this framework. In
253*04a1c1a1SRobert Mustacchi * general, they'd want to be able to at least send a full erased page in a
254*04a1c1a1SRobert Mustacchi * single I/O. They also have a different bus utilization as compared to a 100
255*04a1c1a1SRobert Mustacchi * kHz I2C standard speed, those devices usually run at least at 10 MHz if not
256*04a1c1a1SRobert Mustacchi * faster.
257*04a1c1a1SRobert Mustacchi */
258*04a1c1a1SRobert Mustacchi static uint32_t eedev_default_max_io = 128;
259*04a1c1a1SRobert Mustacchi
260*04a1c1a1SRobert Mustacchi static eedev_dip_t *
eedev_dip_find(dev_info_t * dip)261*04a1c1a1SRobert Mustacchi eedev_dip_find(dev_info_t *dip)
262*04a1c1a1SRobert Mustacchi {
263*04a1c1a1SRobert Mustacchi VERIFY(MUTEX_HELD(&eedev.eedev_mutex));
264*04a1c1a1SRobert Mustacchi for (eedev_dip_t *e = list_head(&eedev.eedev_dips); e != NULL;
265*04a1c1a1SRobert Mustacchi e = list_next(&eedev.eedev_dips, e)) {
266*04a1c1a1SRobert Mustacchi if (dip == e->ed_dip) {
267*04a1c1a1SRobert Mustacchi return (e);
268*04a1c1a1SRobert Mustacchi }
269*04a1c1a1SRobert Mustacchi }
270*04a1c1a1SRobert Mustacchi
271*04a1c1a1SRobert Mustacchi return (NULL);
272*04a1c1a1SRobert Mustacchi }
273*04a1c1a1SRobert Mustacchi
274*04a1c1a1SRobert Mustacchi /*
275*04a1c1a1SRobert Mustacchi * This is used in the various operations to look up an existing eedev based on
276*04a1c1a1SRobert Mustacchi * its dev_t. This is meant to be used by everything other than open(9E), as it
277*04a1c1a1SRobert Mustacchi * will assume that a hold already exists.
278*04a1c1a1SRobert Mustacchi */
279*04a1c1a1SRobert Mustacchi static eedev_hdl_t *
eedev_lookup_by_id(dev_t dev)280*04a1c1a1SRobert Mustacchi eedev_lookup_by_id(dev_t dev)
281*04a1c1a1SRobert Mustacchi {
282*04a1c1a1SRobert Mustacchi mutex_enter(&eedev.eedev_mutex);
283*04a1c1a1SRobert Mustacchi for (eedev_hdl_t *h = list_head(&eedev.eedev_list); h != NULL;
284*04a1c1a1SRobert Mustacchi h = list_next(&eedev.eedev_list, h)) {
285*04a1c1a1SRobert Mustacchi if (h->eh_dev != dev)
286*04a1c1a1SRobert Mustacchi continue;
287*04a1c1a1SRobert Mustacchi
288*04a1c1a1SRobert Mustacchi if ((h->eh_flags & EEDEV_F_HELD) == 0)
289*04a1c1a1SRobert Mustacchi break;
290*04a1c1a1SRobert Mustacchi
291*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
292*04a1c1a1SRobert Mustacchi return (h);
293*04a1c1a1SRobert Mustacchi }
294*04a1c1a1SRobert Mustacchi
295*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
296*04a1c1a1SRobert Mustacchi return (NULL);
297*04a1c1a1SRobert Mustacchi }
298*04a1c1a1SRobert Mustacchi
299*04a1c1a1SRobert Mustacchi /*
300*04a1c1a1SRobert Mustacchi * We are called here by one or more threads that are trying to open a specific
301*04a1c1a1SRobert Mustacchi * eeprom. When an eeprom is opened, we may need to cons the provider driver
302*04a1c1a1SRobert Mustacchi * back into existence. We serialize opens, but also have to drop all of our
303*04a1c1a1SRobert Mustacchi * locks along the way.
304*04a1c1a1SRobert Mustacchi *
305*04a1c1a1SRobert Mustacchi * While multiple threads can call open() on the same minor, we will only
306*04a1c1a1SRobert Mustacchi * receive a single close. Therefore, we also need to make sure that we don't go
307*04a1c1a1SRobert Mustacchi * overboard and put too many references on.
308*04a1c1a1SRobert Mustacchi */
309*04a1c1a1SRobert Mustacchi static int
eedev_hold_by_id(dev_t dev)310*04a1c1a1SRobert Mustacchi eedev_hold_by_id(dev_t dev)
311*04a1c1a1SRobert Mustacchi {
312*04a1c1a1SRobert Mustacchi eedev_hdl_t *hdl = NULL;
313*04a1c1a1SRobert Mustacchi
314*04a1c1a1SRobert Mustacchi mutex_enter(&eedev.eedev_mutex);
315*04a1c1a1SRobert Mustacchi for (eedev_hdl_t *h = list_head(&eedev.eedev_list); h != NULL;
316*04a1c1a1SRobert Mustacchi h = list_next(&eedev.eedev_list, h)) {
317*04a1c1a1SRobert Mustacchi if (h->eh_dev == dev) {
318*04a1c1a1SRobert Mustacchi hdl = h;
319*04a1c1a1SRobert Mustacchi break;
320*04a1c1a1SRobert Mustacchi }
321*04a1c1a1SRobert Mustacchi }
322*04a1c1a1SRobert Mustacchi
323*04a1c1a1SRobert Mustacchi if (hdl == NULL) {
324*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
325*04a1c1a1SRobert Mustacchi return (ESTALE);
326*04a1c1a1SRobert Mustacchi }
327*04a1c1a1SRobert Mustacchi
328*04a1c1a1SRobert Mustacchi restart:
329*04a1c1a1SRobert Mustacchi if ((hdl->eh_dip->ed_flags & EEDEV_DIP_F_REMOVED) != 0) {
330*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
331*04a1c1a1SRobert Mustacchi return (ESTALE);
332*04a1c1a1SRobert Mustacchi }
333*04a1c1a1SRobert Mustacchi
334*04a1c1a1SRobert Mustacchi /*
335*04a1c1a1SRobert Mustacchi * We have our eeprom. If it's already held, then there's nothing more
336*04a1c1a1SRobert Mustacchi * for us to do. The kernel guarantees that it won't call close() on
337*04a1c1a1SRobert Mustacchi * this dev_t while open() is running. If it's not both held and usable
338*04a1c1a1SRobert Mustacchi * then there is work to do.
339*04a1c1a1SRobert Mustacchi */
340*04a1c1a1SRobert Mustacchi const eedev_flags_t targ = EEDEV_F_HELD | EEDEV_F_USABLE;
341*04a1c1a1SRobert Mustacchi if ((hdl->eh_flags & targ) == targ) {
342*04a1c1a1SRobert Mustacchi VERIFY0(hdl->eh_flags & EEDEV_F_BUSY);
343*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
344*04a1c1a1SRobert Mustacchi return (0);
345*04a1c1a1SRobert Mustacchi }
346*04a1c1a1SRobert Mustacchi
347*04a1c1a1SRobert Mustacchi /*
348*04a1c1a1SRobert Mustacchi * This eeprom isn't both held and usable right now. That means we would
349*04a1c1a1SRobert Mustacchi * like to hold it and potentially reattach the provider, which means
350*04a1c1a1SRobert Mustacchi * entering its parent NDI locks. We will indicate that we're trying to
351*04a1c1a1SRobert Mustacchi * use this node and serialize this.
352*04a1c1a1SRobert Mustacchi */
353*04a1c1a1SRobert Mustacchi if ((hdl->eh_flags & EEDEV_F_BUSY) != 0) {
354*04a1c1a1SRobert Mustacchi hdl->eh_nwaiters++;
355*04a1c1a1SRobert Mustacchi while ((hdl->eh_flags & EEDEV_F_BUSY) != 0) {
356*04a1c1a1SRobert Mustacchi int cv = cv_wait_sig(&hdl->eh_cv, &eedev.eedev_mutex);
357*04a1c1a1SRobert Mustacchi if (cv == 0) {
358*04a1c1a1SRobert Mustacchi hdl->eh_nwaiters--;
359*04a1c1a1SRobert Mustacchi cv_broadcast(&hdl->eh_cv);
360*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
361*04a1c1a1SRobert Mustacchi return (EINTR);
362*04a1c1a1SRobert Mustacchi }
363*04a1c1a1SRobert Mustacchi }
364*04a1c1a1SRobert Mustacchi hdl->eh_nwaiters--;
365*04a1c1a1SRobert Mustacchi goto restart;
366*04a1c1a1SRobert Mustacchi }
367*04a1c1a1SRobert Mustacchi
368*04a1c1a1SRobert Mustacchi /*
369*04a1c1a1SRobert Mustacchi * We technically have ownership of this node now. Set that we're trying
370*04a1c1a1SRobert Mustacchi * to be the ones to hold it.
371*04a1c1a1SRobert Mustacchi */
372*04a1c1a1SRobert Mustacchi hdl->eh_flags |= EEDEV_F_BUSY;
373*04a1c1a1SRobert Mustacchi dev_info_t *pdip = ddi_get_parent(hdl->eh_dip->ed_dip);
374*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
375*04a1c1a1SRobert Mustacchi
376*04a1c1a1SRobert Mustacchi ndi_devi_enter(pdip);
377*04a1c1a1SRobert Mustacchi e_ddi_hold_devi(hdl->eh_dip->ed_dip);
378*04a1c1a1SRobert Mustacchi ndi_devi_exit(pdip);
379*04a1c1a1SRobert Mustacchi
380*04a1c1a1SRobert Mustacchi /*
381*04a1c1a1SRobert Mustacchi * Now that we have an NDI hold, check if this is valid or not. There's
382*04a1c1a1SRobert Mustacchi * a chance we were racing with a detach.
383*04a1c1a1SRobert Mustacchi */
384*04a1c1a1SRobert Mustacchi mutex_enter(&eedev.eedev_mutex);
385*04a1c1a1SRobert Mustacchi hdl->eh_flags |= EEDEV_F_HELD;
386*04a1c1a1SRobert Mustacchi
387*04a1c1a1SRobert Mustacchi if ((hdl->eh_dip->ed_flags & EEDEV_DIP_F_REMOVED) != 0) {
388*04a1c1a1SRobert Mustacchi hdl->eh_flags &= ~(EEDEV_F_HELD | EEDEV_F_BUSY);
389*04a1c1a1SRobert Mustacchi cv_broadcast(&hdl->eh_cv);
390*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
391*04a1c1a1SRobert Mustacchi ddi_release_devi(hdl->eh_dip->ed_dip);
392*04a1c1a1SRobert Mustacchi return (ESTALE);
393*04a1c1a1SRobert Mustacchi }
394*04a1c1a1SRobert Mustacchi
395*04a1c1a1SRobert Mustacchi /*
396*04a1c1a1SRobert Mustacchi * If it's not usable, try to configure the driver. This requires us to
397*04a1c1a1SRobert Mustacchi * drop the lock again, and thus have another chance of a race
398*04a1c1a1SRobert Mustacchi * condition.
399*04a1c1a1SRobert Mustacchi */
400*04a1c1a1SRobert Mustacchi if ((hdl->eh_dip->ed_flags & EEDEV_F_USABLE) == 0) {
401*04a1c1a1SRobert Mustacchi dev_info_t *child;
402*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
403*04a1c1a1SRobert Mustacchi if (ndi_devi_config_one(pdip, hdl->eh_dip->ed_ua, &child,
404*04a1c1a1SRobert Mustacchi NDI_CONFIG | NDI_ONLINE_ATTACH | NDI_NO_EVENT) ==
405*04a1c1a1SRobert Mustacchi NDI_SUCCESS) {
406*04a1c1a1SRobert Mustacchi /*
407*04a1c1a1SRobert Mustacchi * When this is successful, a hold on the child is
408*04a1c1a1SRobert Mustacchi * placed. We already have one. Release this one.
409*04a1c1a1SRobert Mustacchi */
410*04a1c1a1SRobert Mustacchi ddi_release_devi(child);
411*04a1c1a1SRobert Mustacchi }
412*04a1c1a1SRobert Mustacchi mutex_enter(&eedev.eedev_mutex);
413*04a1c1a1SRobert Mustacchi
414*04a1c1a1SRobert Mustacchi if ((hdl->eh_dip->ed_flags & EEDEV_DIP_F_REMOVED) != 0 ||
415*04a1c1a1SRobert Mustacchi (hdl->eh_flags & EEDEV_F_USABLE) == 0) {
416*04a1c1a1SRobert Mustacchi hdl->eh_flags &= ~(EEDEV_F_HELD | EEDEV_F_BUSY);
417*04a1c1a1SRobert Mustacchi cv_broadcast(&hdl->eh_cv);
418*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
419*04a1c1a1SRobert Mustacchi ddi_release_devi(hdl->eh_dip->ed_dip);
420*04a1c1a1SRobert Mustacchi return (ESTALE);
421*04a1c1a1SRobert Mustacchi }
422*04a1c1a1SRobert Mustacchi }
423*04a1c1a1SRobert Mustacchi
424*04a1c1a1SRobert Mustacchi hdl->eh_flags &= ~EEDEV_F_BUSY;
425*04a1c1a1SRobert Mustacchi cv_broadcast(&hdl->eh_cv);
426*04a1c1a1SRobert Mustacchi VERIFY3U(hdl->eh_flags & targ, ==, targ);
427*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
428*04a1c1a1SRobert Mustacchi
429*04a1c1a1SRobert Mustacchi return (0);
430*04a1c1a1SRobert Mustacchi }
431*04a1c1a1SRobert Mustacchi
432*04a1c1a1SRobert Mustacchi static void
eedev_dip_free(eedev_dip_t * e)433*04a1c1a1SRobert Mustacchi eedev_dip_free(eedev_dip_t *e)
434*04a1c1a1SRobert Mustacchi {
435*04a1c1a1SRobert Mustacchi list_destroy(&e->ed_devs);
436*04a1c1a1SRobert Mustacchi strfree(e->ed_ua);
437*04a1c1a1SRobert Mustacchi kmem_free(e, sizeof (eedev_dip_t));
438*04a1c1a1SRobert Mustacchi }
439*04a1c1a1SRobert Mustacchi
440*04a1c1a1SRobert Mustacchi static void
eedev_free(eedev_hdl_t * eh)441*04a1c1a1SRobert Mustacchi eedev_free(eedev_hdl_t *eh)
442*04a1c1a1SRobert Mustacchi {
443*04a1c1a1SRobert Mustacchi if ((eh->eh_flags & EEDEV_F_MINOR_VALID) != 0) {
444*04a1c1a1SRobert Mustacchi ddi_remove_minor_node(eedev.eedev_dip, eh->eh_name);
445*04a1c1a1SRobert Mustacchi eh->eh_flags &= ~EEDEV_F_MINOR_VALID;
446*04a1c1a1SRobert Mustacchi }
447*04a1c1a1SRobert Mustacchi
448*04a1c1a1SRobert Mustacchi if ((eh->eh_flags & EEDEV_F_MINOR_PROPS) != 0) {
449*04a1c1a1SRobert Mustacchi (void) ddi_prop_remove(eh->eh_dev, eedev.eedev_dip, "Size");
450*04a1c1a1SRobert Mustacchi eh->eh_flags &= ~EEDEV_F_MINOR_PROPS;
451*04a1c1a1SRobert Mustacchi }
452*04a1c1a1SRobert Mustacchi
453*04a1c1a1SRobert Mustacchi if ((eh->eh_flags & EEDEV_F_ID_ALLOC) != 0) {
454*04a1c1a1SRobert Mustacchi id_free(eedev.eedev_idspace, eh->eh_minor);
455*04a1c1a1SRobert Mustacchi }
456*04a1c1a1SRobert Mustacchi
457*04a1c1a1SRobert Mustacchi strfree(eh->eh_name);
458*04a1c1a1SRobert Mustacchi cv_destroy(&eh->eh_cv);
459*04a1c1a1SRobert Mustacchi kmem_free(eh, sizeof (eedev_hdl_t));
460*04a1c1a1SRobert Mustacchi }
461*04a1c1a1SRobert Mustacchi
462*04a1c1a1SRobert Mustacchi void
eedev_fini(eedev_hdl_t * eh)463*04a1c1a1SRobert Mustacchi eedev_fini(eedev_hdl_t *eh)
464*04a1c1a1SRobert Mustacchi {
465*04a1c1a1SRobert Mustacchi if (eh == NULL) {
466*04a1c1a1SRobert Mustacchi return;
467*04a1c1a1SRobert Mustacchi }
468*04a1c1a1SRobert Mustacchi
469*04a1c1a1SRobert Mustacchi mutex_enter(&eedev.eedev_mutex);
470*04a1c1a1SRobert Mustacchi VERIFY0(eh->eh_flags & EEDEV_F_HELD);
471*04a1c1a1SRobert Mustacchi VERIFY0(eh->eh_flags & EEDEV_F_BUSY);
472*04a1c1a1SRobert Mustacchi VERIFY3U(eh->eh_flags & EEDEV_F_USABLE, !=, 0);
473*04a1c1a1SRobert Mustacchi eh->eh_flags &= ~EEDEV_F_USABLE;
474*04a1c1a1SRobert Mustacchi eh->eh_ops = NULL;
475*04a1c1a1SRobert Mustacchi eh->eh_driver = NULL;
476*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
477*04a1c1a1SRobert Mustacchi }
478*04a1c1a1SRobert Mustacchi
479*04a1c1a1SRobert Mustacchi static void
eedev_dip_unbind_taskq(void * arg)480*04a1c1a1SRobert Mustacchi eedev_dip_unbind_taskq(void *arg)
481*04a1c1a1SRobert Mustacchi {
482*04a1c1a1SRobert Mustacchi eedev_hdl_t *hdl;
483*04a1c1a1SRobert Mustacchi eedev_dip_t *ed = arg;
484*04a1c1a1SRobert Mustacchi
485*04a1c1a1SRobert Mustacchi mutex_enter(&eedev.eedev_mutex);
486*04a1c1a1SRobert Mustacchi while ((hdl = list_remove_head(&ed->ed_devs)) != NULL) {
487*04a1c1a1SRobert Mustacchi while ((hdl->eh_flags & EEDEV_F_BUSY) != 0 ||
488*04a1c1a1SRobert Mustacchi hdl->eh_nwaiters > 0) {
489*04a1c1a1SRobert Mustacchi cv_wait(&hdl->eh_cv, &eedev.eedev_mutex);
490*04a1c1a1SRobert Mustacchi }
491*04a1c1a1SRobert Mustacchi eedev_free(hdl);
492*04a1c1a1SRobert Mustacchi }
493*04a1c1a1SRobert Mustacchi
494*04a1c1a1SRobert Mustacchi /*
495*04a1c1a1SRobert Mustacchi * Ensure that any stale minors that we've created have been removed.
496*04a1c1a1SRobert Mustacchi */
497*04a1c1a1SRobert Mustacchi (void) devfs_clean(ddi_get_parent(eedev.eedev_dip), NULL, 0);
498*04a1c1a1SRobert Mustacchi eedev_dip_free(ed);
499*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
500*04a1c1a1SRobert Mustacchi }
501*04a1c1a1SRobert Mustacchi
502*04a1c1a1SRobert Mustacchi /*
503*04a1c1a1SRobert Mustacchi * We're being called back because a node is being destroyed. Set that this is
504*04a1c1a1SRobert Mustacchi * being removed, remove them from our global lists, and then dispatch a taskq
505*04a1c1a1SRobert Mustacchi * to finish clean up outside of the actual NDI context.
506*04a1c1a1SRobert Mustacchi */
507*04a1c1a1SRobert Mustacchi static void
eedev_dip_unbind_cb(void * arg,dev_info_t * dip)508*04a1c1a1SRobert Mustacchi eedev_dip_unbind_cb(void *arg, dev_info_t *dip)
509*04a1c1a1SRobert Mustacchi {
510*04a1c1a1SRobert Mustacchi eedev_dip_t *ed = arg;
511*04a1c1a1SRobert Mustacchi
512*04a1c1a1SRobert Mustacchi mutex_enter(&eedev.eedev_mutex);
513*04a1c1a1SRobert Mustacchi ed->ed_flags |= EEDEV_DIP_F_REMOVED;
514*04a1c1a1SRobert Mustacchi list_remove(&eedev.eedev_dips, ed);
515*04a1c1a1SRobert Mustacchi
516*04a1c1a1SRobert Mustacchi for (eedev_hdl_t *h = list_head(&ed->ed_devs); h != NULL;
517*04a1c1a1SRobert Mustacchi h = list_next(&ed->ed_devs, h)) {
518*04a1c1a1SRobert Mustacchi list_remove(&eedev.eedev_list, h);
519*04a1c1a1SRobert Mustacchi }
520*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
521*04a1c1a1SRobert Mustacchi
522*04a1c1a1SRobert Mustacchi (void) taskq_dispatch(system_taskq, eedev_dip_unbind_taskq, ed,
523*04a1c1a1SRobert Mustacchi TQ_SLEEP);
524*04a1c1a1SRobert Mustacchi }
525*04a1c1a1SRobert Mustacchi
526*04a1c1a1SRobert Mustacchi static eedev_dip_t *
eedev_dip_create(dev_info_t * dip)527*04a1c1a1SRobert Mustacchi eedev_dip_create(dev_info_t *dip)
528*04a1c1a1SRobert Mustacchi {
529*04a1c1a1SRobert Mustacchi eedev_dip_t *e;
530*04a1c1a1SRobert Mustacchi
531*04a1c1a1SRobert Mustacchi e = kmem_zalloc(sizeof (eedev_dip_t), KM_SLEEP);
532*04a1c1a1SRobert Mustacchi e->ed_dip = dip;
533*04a1c1a1SRobert Mustacchi e->ed_ua = kmem_asprintf("%s@%s", ddi_node_name(dip),
534*04a1c1a1SRobert Mustacchi ddi_get_name_addr(dip));
535*04a1c1a1SRobert Mustacchi e->ed_cb.ddiub_cb = eedev_dip_unbind_cb;
536*04a1c1a1SRobert Mustacchi e->ed_cb.ddiub_arg = e;
537*04a1c1a1SRobert Mustacchi list_create(&e->ed_devs, sizeof (eedev_hdl_t),
538*04a1c1a1SRobert Mustacchi offsetof(eedev_hdl_t, eh_dip_link));
539*04a1c1a1SRobert Mustacchi e_ddi_register_unbind_callback(dip, &e->ed_cb);
540*04a1c1a1SRobert Mustacchi
541*04a1c1a1SRobert Mustacchi return (e);
542*04a1c1a1SRobert Mustacchi }
543*04a1c1a1SRobert Mustacchi
544*04a1c1a1SRobert Mustacchi static bool
eedev_minor_create(eedev_hdl_t * hdl)545*04a1c1a1SRobert Mustacchi eedev_minor_create(eedev_hdl_t *hdl)
546*04a1c1a1SRobert Mustacchi {
547*04a1c1a1SRobert Mustacchi VERIFY(MUTEX_HELD(&eedev.eedev_mutex));
548*04a1c1a1SRobert Mustacchi
549*04a1c1a1SRobert Mustacchi hdl->eh_dev = makedevice(ddi_driver_major(eedev.eedev_dip),
550*04a1c1a1SRobert Mustacchi hdl->eh_minor);
551*04a1c1a1SRobert Mustacchi
552*04a1c1a1SRobert Mustacchi if ((hdl->eh_flags & EEDEV_F_MINOR_PROPS) == 0) {
553*04a1c1a1SRobert Mustacchi if (ddi_prop_update_int64(hdl->eh_dev, eedev.eedev_dip, "Size",
554*04a1c1a1SRobert Mustacchi hdl->eh_size) != DDI_PROP_SUCCESS) {
555*04a1c1a1SRobert Mustacchi dev_err(eedev.eedev_dip, CE_WARN, "!failed to set Size "
556*04a1c1a1SRobert Mustacchi "property for minor %s (%d) for %s%d", hdl->eh_name,
557*04a1c1a1SRobert Mustacchi hdl->eh_minor, ddi_driver_name(hdl->eh_dip->ed_dip),
558*04a1c1a1SRobert Mustacchi ddi_get_instance(hdl->eh_dip->ed_dip));
559*04a1c1a1SRobert Mustacchi return (false);
560*04a1c1a1SRobert Mustacchi }
561*04a1c1a1SRobert Mustacchi hdl->eh_flags |= EEDEV_F_MINOR_PROPS;
562*04a1c1a1SRobert Mustacchi }
563*04a1c1a1SRobert Mustacchi
564*04a1c1a1SRobert Mustacchi if ((hdl->eh_flags & EEDEV_F_MINOR_VALID) == 0) {
565*04a1c1a1SRobert Mustacchi if (ddi_create_minor_node(eedev.eedev_dip, hdl->eh_name,
566*04a1c1a1SRobert Mustacchi S_IFCHR, hdl->eh_minor, DDI_NT_EEPROM, 0) != DDI_SUCCESS) {
567*04a1c1a1SRobert Mustacchi dev_err(eedev.eedev_dip, CE_WARN, "!failed to create "
568*04a1c1a1SRobert Mustacchi "eeprom minor %s (%d) for %s%d", hdl->eh_name,
569*04a1c1a1SRobert Mustacchi hdl->eh_minor, ddi_driver_name(hdl->eh_dip->ed_dip),
570*04a1c1a1SRobert Mustacchi ddi_get_instance(hdl->eh_dip->ed_dip));
571*04a1c1a1SRobert Mustacchi return (false);
572*04a1c1a1SRobert Mustacchi }
573*04a1c1a1SRobert Mustacchi }
574*04a1c1a1SRobert Mustacchi
575*04a1c1a1SRobert Mustacchi hdl->eh_flags |= EEDEV_F_MINOR_VALID;
576*04a1c1a1SRobert Mustacchi return (true);
577*04a1c1a1SRobert Mustacchi }
578*04a1c1a1SRobert Mustacchi
579*04a1c1a1SRobert Mustacchi int
eedev_create(const eedev_reg_t * reg,eedev_hdl_t ** hdlp)580*04a1c1a1SRobert Mustacchi eedev_create(const eedev_reg_t *reg, eedev_hdl_t **hdlp)
581*04a1c1a1SRobert Mustacchi {
582*04a1c1a1SRobert Mustacchi eedev_hdl_t *hdl;
583*04a1c1a1SRobert Mustacchi eedev_dip_t *dip;
584*04a1c1a1SRobert Mustacchi char *name;
585*04a1c1a1SRobert Mustacchi
586*04a1c1a1SRobert Mustacchi if (reg->ereg_vers != EEDEV_REG_VERS0) {
587*04a1c1a1SRobert Mustacchi return (ENOTSUP);
588*04a1c1a1SRobert Mustacchi }
589*04a1c1a1SRobert Mustacchi
590*04a1c1a1SRobert Mustacchi if (reg->ereg_size == 0 || reg->ereg_dip == NULL ||
591*04a1c1a1SRobert Mustacchi reg->ereg_ops == NULL || reg->ereg_ops->eo_read == NULL) {
592*04a1c1a1SRobert Mustacchi return (EINVAL);
593*04a1c1a1SRobert Mustacchi }
594*04a1c1a1SRobert Mustacchi
595*04a1c1a1SRobert Mustacchi if (!reg->ereg_ro && reg->ereg_ops->eo_write == NULL) {
596*04a1c1a1SRobert Mustacchi return (EINVAL);
597*04a1c1a1SRobert Mustacchi }
598*04a1c1a1SRobert Mustacchi
599*04a1c1a1SRobert Mustacchi if (reg->ereg_seg > reg->ereg_size ||
600*04a1c1a1SRobert Mustacchi reg->ereg_read_gran > reg->ereg_size ||
601*04a1c1a1SRobert Mustacchi reg->ereg_write_gran > reg->ereg_size) {
602*04a1c1a1SRobert Mustacchi return (EINVAL);
603*04a1c1a1SRobert Mustacchi }
604*04a1c1a1SRobert Mustacchi
605*04a1c1a1SRobert Mustacchi if (reg->ereg_name != NULL) {
606*04a1c1a1SRobert Mustacchi size_t len = strnlen(reg->ereg_name, EEDEV_NAME_MAX);
607*04a1c1a1SRobert Mustacchi if (len >= EEDEV_NAME_MAX || len == 0) {
608*04a1c1a1SRobert Mustacchi return (EINVAL);
609*04a1c1a1SRobert Mustacchi }
610*04a1c1a1SRobert Mustacchi
611*04a1c1a1SRobert Mustacchi for (size_t i = 0; i < len; i++) {
612*04a1c1a1SRobert Mustacchi if (!ISALNUM(reg->ereg_name[i])) {
613*04a1c1a1SRobert Mustacchi return (EINVAL);
614*04a1c1a1SRobert Mustacchi }
615*04a1c1a1SRobert Mustacchi }
616*04a1c1a1SRobert Mustacchi }
617*04a1c1a1SRobert Mustacchi
618*04a1c1a1SRobert Mustacchi mutex_enter(&eedev.eedev_mutex);
619*04a1c1a1SRobert Mustacchi
620*04a1c1a1SRobert Mustacchi /*
621*04a1c1a1SRobert Mustacchi * Make sure the dip tracking this exists so we can bring this device
622*04a1c1a1SRobert Mustacchi * back if required.
623*04a1c1a1SRobert Mustacchi */
624*04a1c1a1SRobert Mustacchi dip = eedev_dip_find(reg->ereg_dip);
625*04a1c1a1SRobert Mustacchi if (dip == NULL) {
626*04a1c1a1SRobert Mustacchi dip = eedev_dip_create(reg->ereg_dip);
627*04a1c1a1SRobert Mustacchi list_insert_tail(&eedev.eedev_dips, dip);
628*04a1c1a1SRobert Mustacchi }
629*04a1c1a1SRobert Mustacchi
630*04a1c1a1SRobert Mustacchi if (reg->ereg_name != NULL) {
631*04a1c1a1SRobert Mustacchi name = kmem_asprintf("%s:%d:%s", ddi_driver_name(reg->ereg_dip),
632*04a1c1a1SRobert Mustacchi ddi_get_instance(reg->ereg_dip), reg->ereg_name);
633*04a1c1a1SRobert Mustacchi } else {
634*04a1c1a1SRobert Mustacchi name = kmem_asprintf("%s:%d:eeprom",
635*04a1c1a1SRobert Mustacchi ddi_driver_name(reg->ereg_dip),
636*04a1c1a1SRobert Mustacchi ddi_get_instance(reg->ereg_dip));
637*04a1c1a1SRobert Mustacchi }
638*04a1c1a1SRobert Mustacchi
639*04a1c1a1SRobert Mustacchi /*
640*04a1c1a1SRobert Mustacchi * Check to see if this handle is something that's come back from the
641*04a1c1a1SRobert Mustacchi * first time it was created because it was reattached.
642*04a1c1a1SRobert Mustacchi */
643*04a1c1a1SRobert Mustacchi hdl = NULL;
644*04a1c1a1SRobert Mustacchi for (eedev_hdl_t *h = list_head(&dip->ed_devs); h != NULL;
645*04a1c1a1SRobert Mustacchi h = list_next(&dip->ed_devs, h)) {
646*04a1c1a1SRobert Mustacchi if (strcmp(h->eh_name, name) == 0) {
647*04a1c1a1SRobert Mustacchi hdl = h;
648*04a1c1a1SRobert Mustacchi break;
649*04a1c1a1SRobert Mustacchi }
650*04a1c1a1SRobert Mustacchi }
651*04a1c1a1SRobert Mustacchi
652*04a1c1a1SRobert Mustacchi if (hdl != NULL) {
653*04a1c1a1SRobert Mustacchi VERIFY0(hdl->eh_flags & EEDEV_F_USABLE);
654*04a1c1a1SRobert Mustacchi
655*04a1c1a1SRobert Mustacchi strfree(name);
656*04a1c1a1SRobert Mustacchi name = NULL;
657*04a1c1a1SRobert Mustacchi hdl->eh_ops = reg->ereg_ops;
658*04a1c1a1SRobert Mustacchi hdl->eh_driver = reg->ereg_driver;
659*04a1c1a1SRobert Mustacchi
660*04a1c1a1SRobert Mustacchi VERIFY3U(hdl->eh_size, ==, reg->ereg_size);
661*04a1c1a1SRobert Mustacchi VERIFY3U(hdl->eh_seg, ==, reg->ereg_seg);
662*04a1c1a1SRobert Mustacchi VERIFY3U(hdl->eh_read_gran, ==, reg->ereg_read_gran);
663*04a1c1a1SRobert Mustacchi VERIFY3U(hdl->eh_write_gran, ==, reg->ereg_write_gran);
664*04a1c1a1SRobert Mustacchi if (reg->ereg_max_read != 0) {
665*04a1c1a1SRobert Mustacchi VERIFY3U(hdl->eh_max_read, ==, reg->ereg_max_read);
666*04a1c1a1SRobert Mustacchi }
667*04a1c1a1SRobert Mustacchi
668*04a1c1a1SRobert Mustacchi if (reg->ereg_max_write != 0) {
669*04a1c1a1SRobert Mustacchi VERIFY3U(hdl->eh_max_write, ==, reg->ereg_max_write);
670*04a1c1a1SRobert Mustacchi }
671*04a1c1a1SRobert Mustacchi } else {
672*04a1c1a1SRobert Mustacchi hdl = kmem_zalloc(sizeof (eedev_hdl_t), KM_SLEEP);
673*04a1c1a1SRobert Mustacchi cv_init(&hdl->eh_cv, NULL, CV_DRIVER, NULL);
674*04a1c1a1SRobert Mustacchi hdl->eh_dip = dip;
675*04a1c1a1SRobert Mustacchi hdl->eh_driver = reg->ereg_driver;
676*04a1c1a1SRobert Mustacchi hdl->eh_name = name;
677*04a1c1a1SRobert Mustacchi name = NULL;
678*04a1c1a1SRobert Mustacchi hdl->eh_ops = reg->ereg_ops;
679*04a1c1a1SRobert Mustacchi hdl->eh_minor = id_alloc_nosleep(eedev.eedev_idspace);
680*04a1c1a1SRobert Mustacchi if (hdl->eh_minor == -1) {
681*04a1c1a1SRobert Mustacchi eedev_free(hdl);
682*04a1c1a1SRobert Mustacchi return (EOVERFLOW);
683*04a1c1a1SRobert Mustacchi }
684*04a1c1a1SRobert Mustacchi hdl->eh_flags |= EEDEV_F_ID_ALLOC;
685*04a1c1a1SRobert Mustacchi
686*04a1c1a1SRobert Mustacchi hdl->eh_ops = reg->ereg_ops;
687*04a1c1a1SRobert Mustacchi hdl->eh_driver = reg->ereg_driver;
688*04a1c1a1SRobert Mustacchi hdl->eh_size = reg->ereg_size;
689*04a1c1a1SRobert Mustacchi hdl->eh_seg = reg->ereg_seg;
690*04a1c1a1SRobert Mustacchi hdl->eh_read_gran = reg->ereg_read_gran;
691*04a1c1a1SRobert Mustacchi hdl->eh_write_gran = reg->ereg_write_gran;
692*04a1c1a1SRobert Mustacchi hdl->eh_max_read = reg->ereg_max_read;
693*04a1c1a1SRobert Mustacchi hdl->eh_max_write = reg->ereg_max_write;
694*04a1c1a1SRobert Mustacchi if (hdl->eh_max_read == 0) {
695*04a1c1a1SRobert Mustacchi hdl->eh_max_read = MIN(eedev_default_max_io,
696*04a1c1a1SRobert Mustacchi hdl->eh_size);
697*04a1c1a1SRobert Mustacchi }
698*04a1c1a1SRobert Mustacchi
699*04a1c1a1SRobert Mustacchi if (hdl->eh_max_write == 0) {
700*04a1c1a1SRobert Mustacchi hdl->eh_max_write = MIN(eedev_default_max_io,
701*04a1c1a1SRobert Mustacchi hdl->eh_size);
702*04a1c1a1SRobert Mustacchi }
703*04a1c1a1SRobert Mustacchi
704*04a1c1a1SRobert Mustacchi if (reg->ereg_ro) {
705*04a1c1a1SRobert Mustacchi hdl->eh_flags |= EEDEV_F_READ_ONLY;
706*04a1c1a1SRobert Mustacchi }
707*04a1c1a1SRobert Mustacchi
708*04a1c1a1SRobert Mustacchi /*
709*04a1c1a1SRobert Mustacchi * Check to make sure that this name is unique across all
710*04a1c1a1SRobert Mustacchi * devices.
711*04a1c1a1SRobert Mustacchi */
712*04a1c1a1SRobert Mustacchi for (eedev_hdl_t *h = list_head(&eedev.eedev_list); h != NULL;
713*04a1c1a1SRobert Mustacchi h = list_next(&eedev.eedev_list, h)) {
714*04a1c1a1SRobert Mustacchi if (strcmp(h->eh_name, hdl->eh_name) == 0) {
715*04a1c1a1SRobert Mustacchi eedev_free(hdl);
716*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
717*04a1c1a1SRobert Mustacchi return (EEXIST);
718*04a1c1a1SRobert Mustacchi }
719*04a1c1a1SRobert Mustacchi }
720*04a1c1a1SRobert Mustacchi
721*04a1c1a1SRobert Mustacchi list_insert_tail(&eedev.eedev_list, hdl);
722*04a1c1a1SRobert Mustacchi list_insert_tail(&dip->ed_devs, hdl);
723*04a1c1a1SRobert Mustacchi }
724*04a1c1a1SRobert Mustacchi
725*04a1c1a1SRobert Mustacchi /*
726*04a1c1a1SRobert Mustacchi * Because we're being called and created, by definition this is usable
727*04a1c1a1SRobert Mustacchi * in the sense that the operations vector and driver has to be valid.
728*04a1c1a1SRobert Mustacchi */
729*04a1c1a1SRobert Mustacchi hdl->eh_flags |= EEDEV_F_USABLE;
730*04a1c1a1SRobert Mustacchi
731*04a1c1a1SRobert Mustacchi if (eedev.eedev_dip != NULL) {
732*04a1c1a1SRobert Mustacchi if (!eedev_minor_create(hdl)) {
733*04a1c1a1SRobert Mustacchi list_remove(&eedev.eedev_list, hdl);
734*04a1c1a1SRobert Mustacchi list_remove(&dip->ed_devs, hdl);
735*04a1c1a1SRobert Mustacchi eedev_free(hdl);
736*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
737*04a1c1a1SRobert Mustacchi return (ENXIO);
738*04a1c1a1SRobert Mustacchi }
739*04a1c1a1SRobert Mustacchi }
740*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
741*04a1c1a1SRobert Mustacchi
742*04a1c1a1SRobert Mustacchi *hdlp = hdl;
743*04a1c1a1SRobert Mustacchi return (0);
744*04a1c1a1SRobert Mustacchi }
745*04a1c1a1SRobert Mustacchi
746*04a1c1a1SRobert Mustacchi static int
eedev_open(dev_t * devp,int flag,int otyp,cred_t * credp)747*04a1c1a1SRobert Mustacchi eedev_open(dev_t *devp, int flag, int otyp, cred_t *credp)
748*04a1c1a1SRobert Mustacchi {
749*04a1c1a1SRobert Mustacchi if (drv_priv(credp) != 0)
750*04a1c1a1SRobert Mustacchi return (EPERM);
751*04a1c1a1SRobert Mustacchi
752*04a1c1a1SRobert Mustacchi if (otyp != OTYP_CHR)
753*04a1c1a1SRobert Mustacchi return (ENOTSUP);
754*04a1c1a1SRobert Mustacchi
755*04a1c1a1SRobert Mustacchi /*
756*04a1c1a1SRobert Mustacchi * In the future we should perform cloning opens to allow for FEXCL
757*04a1c1a1SRobert Mustacchi * support.
758*04a1c1a1SRobert Mustacchi */
759*04a1c1a1SRobert Mustacchi if ((flag & (FNDELAY | FNONBLOCK | FEXCL)) != 0)
760*04a1c1a1SRobert Mustacchi return (EINVAL);
761*04a1c1a1SRobert Mustacchi
762*04a1c1a1SRobert Mustacchi if ((flag & (FREAD | FWRITE)) == 0)
763*04a1c1a1SRobert Mustacchi return (EINVAL);
764*04a1c1a1SRobert Mustacchi
765*04a1c1a1SRobert Mustacchi /*
766*04a1c1a1SRobert Mustacchi * Establish a hold on this if doesn't already exist.
767*04a1c1a1SRobert Mustacchi */
768*04a1c1a1SRobert Mustacchi return (eedev_hold_by_id(*devp));
769*04a1c1a1SRobert Mustacchi }
770*04a1c1a1SRobert Mustacchi
771*04a1c1a1SRobert Mustacchi static int
eedev_read(dev_t dev,struct uio * uio,cred_t * credp)772*04a1c1a1SRobert Mustacchi eedev_read(dev_t dev, struct uio *uio, cred_t *credp)
773*04a1c1a1SRobert Mustacchi {
774*04a1c1a1SRobert Mustacchi uint32_t page, off, nbytes, end;
775*04a1c1a1SRobert Mustacchi eedev_hdl_t *hdl = eedev_lookup_by_id(dev);
776*04a1c1a1SRobert Mustacchi
777*04a1c1a1SRobert Mustacchi if (hdl == NULL)
778*04a1c1a1SRobert Mustacchi return (ENXIO);
779*04a1c1a1SRobert Mustacchi
780*04a1c1a1SRobert Mustacchi if ((uio->uio_fmode & FREAD) == 0)
781*04a1c1a1SRobert Mustacchi return (EBADF);
782*04a1c1a1SRobert Mustacchi
783*04a1c1a1SRobert Mustacchi if ((uio->uio_fmode & (FNONBLOCK | FNDELAY)) != 0)
784*04a1c1a1SRobert Mustacchi return (EINVAL);
785*04a1c1a1SRobert Mustacchi
786*04a1c1a1SRobert Mustacchi /*
787*04a1c1a1SRobert Mustacchi * Determine if this read is aligned. The read granularity
788*04a1c1a1SRobert Mustacchi * basically tells us the units in which the device reads. It
789*04a1c1a1SRobert Mustacchi * must be at least one granularity long and granularity
790*04a1c1a1SRobert Mustacchi * aligned.
791*04a1c1a1SRobert Mustacchi */
792*04a1c1a1SRobert Mustacchi if ((uio->uio_offset % hdl->eh_read_gran) != 0 ||
793*04a1c1a1SRobert Mustacchi (uio->uio_resid % hdl->eh_read_gran) != 0) {
794*04a1c1a1SRobert Mustacchi return (EINVAL);
795*04a1c1a1SRobert Mustacchi }
796*04a1c1a1SRobert Mustacchi
797*04a1c1a1SRobert Mustacchi if (uio->uio_offset >= hdl->eh_size || uio->uio_resid == 0) {
798*04a1c1a1SRobert Mustacchi return (0);
799*04a1c1a1SRobert Mustacchi }
800*04a1c1a1SRobert Mustacchi
801*04a1c1a1SRobert Mustacchi /*
802*04a1c1a1SRobert Mustacchi * Determine if we have a page segment to consider. Devices that do
803*04a1c1a1SRobert Mustacchi * should not cross that in a single I/O.
804*04a1c1a1SRobert Mustacchi */
805*04a1c1a1SRobert Mustacchi if (hdl->eh_seg != 0) {
806*04a1c1a1SRobert Mustacchi page = uio->uio_offset / hdl->eh_seg;
807*04a1c1a1SRobert Mustacchi off = uio->uio_offset % hdl->eh_seg;
808*04a1c1a1SRobert Mustacchi end = (page + 1) * hdl->eh_seg;
809*04a1c1a1SRobert Mustacchi } else {
810*04a1c1a1SRobert Mustacchi page = 0;
811*04a1c1a1SRobert Mustacchi off = uio->uio_offset;
812*04a1c1a1SRobert Mustacchi end = hdl->eh_size;
813*04a1c1a1SRobert Mustacchi }
814*04a1c1a1SRobert Mustacchi
815*04a1c1a1SRobert Mustacchi /*
816*04a1c1a1SRobert Mustacchi * Determine how many bytes to tell the device to read. This is governed
817*04a1c1a1SRobert Mustacchi * by both how many bytes are left in the device / page region and the
818*04a1c1a1SRobert Mustacchi * device's maximum read I/O size.
819*04a1c1a1SRobert Mustacchi */
820*04a1c1a1SRobert Mustacchi nbytes = MIN(uio->uio_resid, end - uio->uio_offset);
821*04a1c1a1SRobert Mustacchi nbytes = MIN(nbytes, hdl->eh_max_read);
822*04a1c1a1SRobert Mustacchi
823*04a1c1a1SRobert Mustacchi return (hdl->eh_ops->eo_read(hdl->eh_driver, uio, page, off, nbytes));
824*04a1c1a1SRobert Mustacchi }
825*04a1c1a1SRobert Mustacchi
826*04a1c1a1SRobert Mustacchi static int
eedev_write(dev_t dev,struct uio * uio,cred_t * credp)827*04a1c1a1SRobert Mustacchi eedev_write(dev_t dev, struct uio *uio, cred_t *credp)
828*04a1c1a1SRobert Mustacchi {
829*04a1c1a1SRobert Mustacchi uint32_t page, off, nbytes, end;
830*04a1c1a1SRobert Mustacchi eedev_hdl_t *hdl = eedev_lookup_by_id(dev);
831*04a1c1a1SRobert Mustacchi
832*04a1c1a1SRobert Mustacchi if (hdl == NULL)
833*04a1c1a1SRobert Mustacchi return (ENXIO);
834*04a1c1a1SRobert Mustacchi
835*04a1c1a1SRobert Mustacchi if ((uio->uio_fmode & FWRITE) == 0)
836*04a1c1a1SRobert Mustacchi return (EBADF);
837*04a1c1a1SRobert Mustacchi
838*04a1c1a1SRobert Mustacchi if ((uio->uio_fmode & (FNONBLOCK | FNDELAY)) != 0)
839*04a1c1a1SRobert Mustacchi return (EINVAL);
840*04a1c1a1SRobert Mustacchi
841*04a1c1a1SRobert Mustacchi /*
842*04a1c1a1SRobert Mustacchi * Determine if this write is aligned. The write granularity
843*04a1c1a1SRobert Mustacchi * basically tells us the units in which the device writes. It
844*04a1c1a1SRobert Mustacchi * must be at least one granularity long and granularity
845*04a1c1a1SRobert Mustacchi * aligned.
846*04a1c1a1SRobert Mustacchi */
847*04a1c1a1SRobert Mustacchi if ((uio->uio_offset % hdl->eh_write_gran) != 0 ||
848*04a1c1a1SRobert Mustacchi (uio->uio_resid % hdl->eh_write_gran) != 0) {
849*04a1c1a1SRobert Mustacchi return (EINVAL);
850*04a1c1a1SRobert Mustacchi }
851*04a1c1a1SRobert Mustacchi
852*04a1c1a1SRobert Mustacchi if (uio->uio_offset >= hdl->eh_size || uio->uio_resid <= 0) {
853*04a1c1a1SRobert Mustacchi return (EINVAL);
854*04a1c1a1SRobert Mustacchi }
855*04a1c1a1SRobert Mustacchi
856*04a1c1a1SRobert Mustacchi /*
857*04a1c1a1SRobert Mustacchi * Determine if we have a page segment to consider. Devices that do
858*04a1c1a1SRobert Mustacchi * should not cross that in a single I/O.
859*04a1c1a1SRobert Mustacchi */
860*04a1c1a1SRobert Mustacchi if (hdl->eh_seg != 0) {
861*04a1c1a1SRobert Mustacchi page = uio->uio_offset / hdl->eh_seg;
862*04a1c1a1SRobert Mustacchi off = uio->uio_offset % hdl->eh_seg;
863*04a1c1a1SRobert Mustacchi end = (page + 1) * hdl->eh_seg;
864*04a1c1a1SRobert Mustacchi } else {
865*04a1c1a1SRobert Mustacchi page = 0;
866*04a1c1a1SRobert Mustacchi off = uio->uio_offset;
867*04a1c1a1SRobert Mustacchi end = hdl->eh_size;
868*04a1c1a1SRobert Mustacchi }
869*04a1c1a1SRobert Mustacchi
870*04a1c1a1SRobert Mustacchi /*
871*04a1c1a1SRobert Mustacchi * Determine how many bytes to tell the device to write. This is
872*04a1c1a1SRobert Mustacchi * governed by both how many bytes are left in the device / page region
873*04a1c1a1SRobert Mustacchi * and the device's maximum write I/O size.
874*04a1c1a1SRobert Mustacchi */
875*04a1c1a1SRobert Mustacchi nbytes = MIN(uio->uio_resid, end - uio->uio_offset);
876*04a1c1a1SRobert Mustacchi nbytes = MIN(nbytes, hdl->eh_max_write);
877*04a1c1a1SRobert Mustacchi
878*04a1c1a1SRobert Mustacchi return (hdl->eh_ops->eo_write(hdl->eh_driver, uio, page, off, nbytes));
879*04a1c1a1SRobert Mustacchi }
880*04a1c1a1SRobert Mustacchi
881*04a1c1a1SRobert Mustacchi static int
eedev_close(dev_t dev,int flag,int otyp,cred_t * credp)882*04a1c1a1SRobert Mustacchi eedev_close(dev_t dev, int flag, int otyp, cred_t *credp)
883*04a1c1a1SRobert Mustacchi {
884*04a1c1a1SRobert Mustacchi eedev_hdl_t *hdl;
885*04a1c1a1SRobert Mustacchi
886*04a1c1a1SRobert Mustacchi if (otyp != OTYP_CHR)
887*04a1c1a1SRobert Mustacchi return (EINVAL);
888*04a1c1a1SRobert Mustacchi
889*04a1c1a1SRobert Mustacchi hdl = eedev_lookup_by_id(dev);
890*04a1c1a1SRobert Mustacchi if (hdl == NULL)
891*04a1c1a1SRobert Mustacchi return (ENXIO);
892*04a1c1a1SRobert Mustacchi
893*04a1c1a1SRobert Mustacchi /*
894*04a1c1a1SRobert Mustacchi * If we support FEXCL tagged cloned opens, then we should clean that up
895*04a1c1a1SRobert Mustacchi * here.
896*04a1c1a1SRobert Mustacchi */
897*04a1c1a1SRobert Mustacchi
898*04a1c1a1SRobert Mustacchi /*
899*04a1c1a1SRobert Mustacchi * This releases our hold on the eeprom provider driver. There may be
900*04a1c1a1SRobert Mustacchi * other holds if there is more than one EEPROM here.
901*04a1c1a1SRobert Mustacchi */
902*04a1c1a1SRobert Mustacchi mutex_enter(&eedev.eedev_mutex);
903*04a1c1a1SRobert Mustacchi VERIFY0(hdl->eh_flags & EEDEV_F_BUSY);
904*04a1c1a1SRobert Mustacchi VERIFY3U(hdl->eh_flags & EEDEV_F_HELD, !=, 0);
905*04a1c1a1SRobert Mustacchi VERIFY3U(hdl->eh_flags & EEDEV_F_USABLE, !=, 0);
906*04a1c1a1SRobert Mustacchi hdl->eh_flags &= ~EEDEV_F_HELD;
907*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
908*04a1c1a1SRobert Mustacchi ddi_release_devi(hdl->eh_dip->ed_dip);
909*04a1c1a1SRobert Mustacchi
910*04a1c1a1SRobert Mustacchi return (0);
911*04a1c1a1SRobert Mustacchi }
912*04a1c1a1SRobert Mustacchi
913*04a1c1a1SRobert Mustacchi static struct cb_ops eedev_cb_ops = {
914*04a1c1a1SRobert Mustacchi .cb_open = eedev_open,
915*04a1c1a1SRobert Mustacchi .cb_close = eedev_close,
916*04a1c1a1SRobert Mustacchi .cb_strategy = nodev,
917*04a1c1a1SRobert Mustacchi .cb_print = nodev,
918*04a1c1a1SRobert Mustacchi .cb_dump = nodev,
919*04a1c1a1SRobert Mustacchi .cb_read = eedev_read,
920*04a1c1a1SRobert Mustacchi .cb_write = eedev_write,
921*04a1c1a1SRobert Mustacchi .cb_ioctl = nodev,
922*04a1c1a1SRobert Mustacchi .cb_devmap = nodev,
923*04a1c1a1SRobert Mustacchi .cb_mmap = nodev,
924*04a1c1a1SRobert Mustacchi .cb_segmap = nodev,
925*04a1c1a1SRobert Mustacchi .cb_chpoll = nochpoll,
926*04a1c1a1SRobert Mustacchi .cb_prop_op = ddi_prop_op,
927*04a1c1a1SRobert Mustacchi .cb_flag = D_MP,
928*04a1c1a1SRobert Mustacchi .cb_rev = CB_REV,
929*04a1c1a1SRobert Mustacchi .cb_aread = nodev,
930*04a1c1a1SRobert Mustacchi .cb_awrite = nodev
931*04a1c1a1SRobert Mustacchi };
932*04a1c1a1SRobert Mustacchi
933*04a1c1a1SRobert Mustacchi
934*04a1c1a1SRobert Mustacchi static int
eedev_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)935*04a1c1a1SRobert Mustacchi eedev_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
936*04a1c1a1SRobert Mustacchi {
937*04a1c1a1SRobert Mustacchi if (cmd == DDI_RESUME) {
938*04a1c1a1SRobert Mustacchi return (DDI_SUCCESS);
939*04a1c1a1SRobert Mustacchi } else if (cmd != DDI_ATTACH) {
940*04a1c1a1SRobert Mustacchi return (DDI_FAILURE);
941*04a1c1a1SRobert Mustacchi }
942*04a1c1a1SRobert Mustacchi
943*04a1c1a1SRobert Mustacchi if (ddi_get_instance(dip) != 0) {
944*04a1c1a1SRobert Mustacchi dev_err(dip, CE_WARN, "only a single instance of eedev is "
945*04a1c1a1SRobert Mustacchi "supported");
946*04a1c1a1SRobert Mustacchi return (DDI_FAILURE);
947*04a1c1a1SRobert Mustacchi }
948*04a1c1a1SRobert Mustacchi
949*04a1c1a1SRobert Mustacchi mutex_enter(&eedev.eedev_mutex);
950*04a1c1a1SRobert Mustacchi VERIFY3P(eedev.eedev_dip, ==, NULL);
951*04a1c1a1SRobert Mustacchi eedev.eedev_dip = dip;
952*04a1c1a1SRobert Mustacchi
953*04a1c1a1SRobert Mustacchi /*
954*04a1c1a1SRobert Mustacchi * It is possible for devices to have registered prior to us being
955*04a1c1a1SRobert Mustacchi * attached. Specifically, modules that use eedev have a dependency on
956*04a1c1a1SRobert Mustacchi * the module, not on an instance. If they have already called
957*04a1c1a1SRobert Mustacchi * eedev_create(), then they will already be in eedev.eedev_list. We
958*04a1c1a1SRobert Mustacchi * need to go through and create a minor node now.
959*04a1c1a1SRobert Mustacchi */
960*04a1c1a1SRobert Mustacchi for (eedev_hdl_t *h = list_head(&eedev.eedev_list); h != NULL;
961*04a1c1a1SRobert Mustacchi h = list_next(&eedev.eedev_list, h)) {
962*04a1c1a1SRobert Mustacchi eedev_flags_t need = EEDEV_F_MINOR_PROPS | EEDEV_F_MINOR_VALID;
963*04a1c1a1SRobert Mustacchi if ((h->eh_flags & need) != need) {
964*04a1c1a1SRobert Mustacchi (void) eedev_minor_create(h);
965*04a1c1a1SRobert Mustacchi }
966*04a1c1a1SRobert Mustacchi }
967*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
968*04a1c1a1SRobert Mustacchi
969*04a1c1a1SRobert Mustacchi return (DDI_SUCCESS);
970*04a1c1a1SRobert Mustacchi }
971*04a1c1a1SRobert Mustacchi
972*04a1c1a1SRobert Mustacchi static int
eedev_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** outp)973*04a1c1a1SRobert Mustacchi eedev_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **outp)
974*04a1c1a1SRobert Mustacchi {
975*04a1c1a1SRobert Mustacchi switch (cmd) {
976*04a1c1a1SRobert Mustacchi case DDI_INFO_DEVT2DEVINFO:
977*04a1c1a1SRobert Mustacchi VERIFY3P(eedev.eedev_dip, !=, NULL);
978*04a1c1a1SRobert Mustacchi *outp = eedev.eedev_dip;
979*04a1c1a1SRobert Mustacchi break;
980*04a1c1a1SRobert Mustacchi case DDI_INFO_DEVT2INSTANCE:
981*04a1c1a1SRobert Mustacchi VERIFY3P(eedev.eedev_dip, !=, NULL);
982*04a1c1a1SRobert Mustacchi *outp = eedev.eedev_dip;
983*04a1c1a1SRobert Mustacchi *outp = (void *)(uintptr_t)ddi_get_instance(eedev.eedev_dip);
984*04a1c1a1SRobert Mustacchi break;
985*04a1c1a1SRobert Mustacchi default:
986*04a1c1a1SRobert Mustacchi return (DDI_FAILURE);
987*04a1c1a1SRobert Mustacchi }
988*04a1c1a1SRobert Mustacchi
989*04a1c1a1SRobert Mustacchi return (DDI_SUCCESS);
990*04a1c1a1SRobert Mustacchi }
991*04a1c1a1SRobert Mustacchi
992*04a1c1a1SRobert Mustacchi static int
eedev_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)993*04a1c1a1SRobert Mustacchi eedev_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
994*04a1c1a1SRobert Mustacchi {
995*04a1c1a1SRobert Mustacchi if (cmd == DDI_SUSPEND) {
996*04a1c1a1SRobert Mustacchi return (DDI_FAILURE);
997*04a1c1a1SRobert Mustacchi } else if (cmd != DDI_DETACH) {
998*04a1c1a1SRobert Mustacchi return (DDI_FAILURE);
999*04a1c1a1SRobert Mustacchi }
1000*04a1c1a1SRobert Mustacchi
1001*04a1c1a1SRobert Mustacchi VERIFY3P(dip, ==, eedev.eedev_dip);
1002*04a1c1a1SRobert Mustacchi mutex_enter(&eedev.eedev_mutex);
1003*04a1c1a1SRobert Mustacchi if (list_is_empty(&eedev.eedev_list)) {
1004*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
1005*04a1c1a1SRobert Mustacchi return (DDI_FAILURE);
1006*04a1c1a1SRobert Mustacchi }
1007*04a1c1a1SRobert Mustacchi
1008*04a1c1a1SRobert Mustacchi ddi_remove_minor_node(eedev.eedev_dip, NULL);
1009*04a1c1a1SRobert Mustacchi eedev.eedev_dip = NULL;
1010*04a1c1a1SRobert Mustacchi mutex_exit(&eedev.eedev_mutex);
1011*04a1c1a1SRobert Mustacchi
1012*04a1c1a1SRobert Mustacchi return (DDI_SUCCESS);
1013*04a1c1a1SRobert Mustacchi }
1014*04a1c1a1SRobert Mustacchi
1015*04a1c1a1SRobert Mustacchi static struct dev_ops eedev_dev_ops = {
1016*04a1c1a1SRobert Mustacchi .devo_rev = DEVO_REV,
1017*04a1c1a1SRobert Mustacchi .devo_refcnt = 0,
1018*04a1c1a1SRobert Mustacchi .devo_getinfo = eedev_getinfo,
1019*04a1c1a1SRobert Mustacchi .devo_identify = nulldev,
1020*04a1c1a1SRobert Mustacchi .devo_probe = nulldev,
1021*04a1c1a1SRobert Mustacchi .devo_attach = eedev_attach,
1022*04a1c1a1SRobert Mustacchi .devo_detach = eedev_detach,
1023*04a1c1a1SRobert Mustacchi .devo_reset = nodev,
1024*04a1c1a1SRobert Mustacchi .devo_quiesce = ddi_quiesce_not_needed,
1025*04a1c1a1SRobert Mustacchi .devo_cb_ops = &eedev_cb_ops
1026*04a1c1a1SRobert Mustacchi };
1027*04a1c1a1SRobert Mustacchi
1028*04a1c1a1SRobert Mustacchi static struct modldrv eedev_modldrv = {
1029*04a1c1a1SRobert Mustacchi .drv_modops = &mod_driverops,
1030*04a1c1a1SRobert Mustacchi .drv_linkinfo = "EEPROM support module",
1031*04a1c1a1SRobert Mustacchi .drv_dev_ops = &eedev_dev_ops
1032*04a1c1a1SRobert Mustacchi };
1033*04a1c1a1SRobert Mustacchi
1034*04a1c1a1SRobert Mustacchi static struct modlinkage eedev_modlinkage = {
1035*04a1c1a1SRobert Mustacchi .ml_rev = MODREV_1,
1036*04a1c1a1SRobert Mustacchi .ml_linkage = { &eedev_modldrv, NULL }
1037*04a1c1a1SRobert Mustacchi };
1038*04a1c1a1SRobert Mustacchi
1039*04a1c1a1SRobert Mustacchi static int
eedev_mod_init(void)1040*04a1c1a1SRobert Mustacchi eedev_mod_init(void)
1041*04a1c1a1SRobert Mustacchi {
1042*04a1c1a1SRobert Mustacchi eedev.eedev_idspace = id_space_create("eedev_minors", EEDEV_MINOR_MIN,
1043*04a1c1a1SRobert Mustacchi EEDEV_MINOR_MAX);
1044*04a1c1a1SRobert Mustacchi if (eedev.eedev_idspace == NULL) {
1045*04a1c1a1SRobert Mustacchi return (ENOMEM);
1046*04a1c1a1SRobert Mustacchi }
1047*04a1c1a1SRobert Mustacchi mutex_init(&eedev.eedev_mutex, NULL, MUTEX_DRIVER, NULL);
1048*04a1c1a1SRobert Mustacchi list_create(&eedev.eedev_list, sizeof (eedev_hdl_t),
1049*04a1c1a1SRobert Mustacchi offsetof(eedev_hdl_t, eh_link));
1050*04a1c1a1SRobert Mustacchi list_create(&eedev.eedev_dips, sizeof (eedev_dip_t),
1051*04a1c1a1SRobert Mustacchi offsetof(eedev_dip_t, ed_link));
1052*04a1c1a1SRobert Mustacchi
1053*04a1c1a1SRobert Mustacchi return (0);
1054*04a1c1a1SRobert Mustacchi }
1055*04a1c1a1SRobert Mustacchi
1056*04a1c1a1SRobert Mustacchi static void
eedev_mod_fini(void)1057*04a1c1a1SRobert Mustacchi eedev_mod_fini(void)
1058*04a1c1a1SRobert Mustacchi {
1059*04a1c1a1SRobert Mustacchi list_destroy(&eedev.eedev_dips);
1060*04a1c1a1SRobert Mustacchi list_destroy(&eedev.eedev_list);
1061*04a1c1a1SRobert Mustacchi mutex_destroy(&eedev.eedev_mutex);
1062*04a1c1a1SRobert Mustacchi id_space_destroy(eedev.eedev_idspace);
1063*04a1c1a1SRobert Mustacchi }
1064*04a1c1a1SRobert Mustacchi
1065*04a1c1a1SRobert Mustacchi int
_init(void)1066*04a1c1a1SRobert Mustacchi _init(void)
1067*04a1c1a1SRobert Mustacchi {
1068*04a1c1a1SRobert Mustacchi int ret;
1069*04a1c1a1SRobert Mustacchi
1070*04a1c1a1SRobert Mustacchi if ((ret = eedev_mod_init()) != 0) {
1071*04a1c1a1SRobert Mustacchi return (ret);
1072*04a1c1a1SRobert Mustacchi }
1073*04a1c1a1SRobert Mustacchi
1074*04a1c1a1SRobert Mustacchi if ((ret = mod_install(&eedev_modlinkage)) != 0) {
1075*04a1c1a1SRobert Mustacchi eedev_mod_fini();
1076*04a1c1a1SRobert Mustacchi }
1077*04a1c1a1SRobert Mustacchi
1078*04a1c1a1SRobert Mustacchi return (ret);
1079*04a1c1a1SRobert Mustacchi }
1080*04a1c1a1SRobert Mustacchi
1081*04a1c1a1SRobert Mustacchi int
_info(struct modinfo * modinfop)1082*04a1c1a1SRobert Mustacchi _info(struct modinfo *modinfop)
1083*04a1c1a1SRobert Mustacchi {
1084*04a1c1a1SRobert Mustacchi return (mod_info(&eedev_modlinkage, modinfop));
1085*04a1c1a1SRobert Mustacchi }
1086*04a1c1a1SRobert Mustacchi
1087*04a1c1a1SRobert Mustacchi int
_fini(void)1088*04a1c1a1SRobert Mustacchi _fini(void)
1089*04a1c1a1SRobert Mustacchi {
1090*04a1c1a1SRobert Mustacchi int ret;
1091*04a1c1a1SRobert Mustacchi
1092*04a1c1a1SRobert Mustacchi if ((ret = mod_remove(&eedev_modlinkage)) == 0) {
1093*04a1c1a1SRobert Mustacchi eedev_mod_fini();
1094*04a1c1a1SRobert Mustacchi }
1095*04a1c1a1SRobert Mustacchi
1096*04a1c1a1SRobert Mustacchi return (ret);
1097*04a1c1a1SRobert Mustacchi }
1098