1 /* $NetBSD: readhappy.c,v 1.1 2015/05/13 07:07:36 pgoyette Exp $ */
2
3 /*-
4 * Copyright (c) 2015 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: readhappy.c,v 1.1 2015/05/13 07:07:36 pgoyette Exp $");
31
32 #include <sys/param.h>
33 #include <sys/conf.h>
34 #include <sys/device.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37
38 /*
39 * Create a device /dev/happy from which you can read sequential
40 * happy numbers.
41 *
42 * To use this device you need to do:
43 * mknod /dev/happy c 210 0
44 *
45 * Commentary:
46 * A happy number is a number defined by the following process: Starting with
47 * any positive integer, replace the number by the sum of the squares of its
48 * digits, and repeat the process until the number equals 1 (where it will
49 * stay), or it loops endlessly in a cycle which does not include 1. Those
50 * numbers for which this process ends in 1 are happy numbers, while those that
51 * do not end in 1 are unhappy numbers (or sad numbers).
52 *
53 * For more information on happy numbers, and the algorithms, see
54 * http://en.wikipedia.org/wiki/Happy_number
55 *
56 * The happy number generator is here only to have something that the user
57 * can read from our device. Any other arbitrary data generator could
58 * have been used. The algorithm is not critical to the implementation
59 * of the module.
60 */
61
62
63 #define HAPPY_NUMBER 1
64
65 /* If n is not happy then its sequence ends in the cycle:
66 * 4, 16, 37, 58, 89, 145, 42, 20, 4, ... */
67 #define SAD_NUMBER 4
68
69 /* Calculate the sum of the squares of the digits of n */
70 static unsigned
dsum(unsigned n)71 dsum(unsigned n)
72 {
73 unsigned sum, x;
74 for (sum = 0; n; n /= 10) {
75 x = n % 10;
76 sum += x * x;
77 }
78 return sum;
79 }
80
81 static int
check_happy(unsigned n)82 check_happy(unsigned n)
83 {
84 for (;;) {
85 unsigned total = dsum(n);
86
87 if (total == HAPPY_NUMBER)
88 return 1;
89 if (total == SAD_NUMBER)
90 return 0;
91
92 n = total;
93 }
94 }
95
96 dev_type_open(happy_open);
97 dev_type_close(happy_close);
98 dev_type_read(happy_read);
99
100 static struct cdevsw happy_cdevsw = {
101 .d_open = happy_open,
102 .d_close = happy_close,
103 .d_read = happy_read,
104 .d_write = nowrite,
105 .d_ioctl = noioctl,
106 .d_stop = nostop,
107 .d_tty = notty,
108 .d_poll = nopoll,
109 .d_mmap = nommap,
110 .d_kqfilter = nokqfilter,
111 .d_discard = nodiscard,
112 .d_flag = D_OTHER
113 };
114
115
116 struct happy_softc {
117 int refcnt;
118 unsigned last;
119 };
120
121 static struct happy_softc sc;
122
123 int
happy_open(dev_t self __unused,int flag __unused,int mode __unused,struct lwp * l __unused)124 happy_open(dev_t self __unused, int flag __unused, int mode __unused,
125 struct lwp *l __unused)
126 {
127 if (sc.refcnt > 0)
128 return EBUSY;
129
130 sc.last = 0;
131 ++sc.refcnt;
132
133 return 0;
134 }
135
136 int
happy_close(dev_t self __unused,int flag __unused,int mode __unused,struct lwp * l __unused)137 happy_close(dev_t self __unused, int flag __unused, int mode __unused,
138 struct lwp *l __unused)
139 {
140 --sc.refcnt;
141
142 return 0;
143 }
144
145 int
happy_read(dev_t self __unused,struct uio * uio,int flags __unused)146 happy_read(dev_t self __unused, struct uio *uio, int flags __unused)
147 {
148 char line[80];
149
150 /* Get next happy number */
151 while (check_happy(++sc.last) == 0)
152 continue;
153
154 /* Print it into line[] with trailing \n */
155 int len = snprintf(line, sizeof(line), "%u\n", sc.last);
156
157 /* Is there room? */
158 if (uio->uio_resid < len) {
159 --sc.last; /* Step back */
160 return EINVAL;
161 }
162
163 /* Send it to User-Space */
164 int e;
165 if ((e = uiomove(line, len, uio)))
166 return e;
167
168 return 0;
169 }
170
171 MODULE(MODULE_CLASS_MISC, happy, NULL);
172
173 static int
happy_modcmd(modcmd_t cmd,void * arg __unused)174 happy_modcmd(modcmd_t cmd, void *arg __unused)
175 {
176 /* The major should be verified and changed if needed to avoid
177 * conflicts with other devices. */
178 int cmajor = 210, bmajor = -1;
179
180 switch (cmd) {
181 case MODULE_CMD_INIT:
182 if (devsw_attach("happy", NULL, &bmajor, &happy_cdevsw,
183 &cmajor))
184 return ENXIO;
185 return 0;
186 case MODULE_CMD_FINI:
187 if (sc.refcnt > 0)
188 return EBUSY;
189
190 devsw_detach(NULL, &happy_cdevsw);
191 return 0;
192 default:
193 return ENOTTY;
194 }
195 }
196