usb_serial.c (213872) | usb_serial.c (214761) |
---|---|
1/* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */ 2 3/*- 4 * Copyright (c) 2001-2003, 2005, 2008 5 * Shunsuke Akiyama <akiyama@jp.FreeBSD.org>. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without --- 14 unchanged lines hidden (view full) --- 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> | 1/* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */ 2 3/*- 4 * Copyright (c) 2001-2003, 2005, 2008 5 * Shunsuke Akiyama <akiyama@jp.FreeBSD.org>. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without --- 14 unchanged lines hidden (view full) --- 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> |
31__FBSDID("$FreeBSD: head/sys/dev/usb/serial/usb_serial.c 213872 2010-10-14 21:45:41Z hselasky $"); | 31__FBSDID("$FreeBSD: head/sys/dev/usb/serial/usb_serial.c 214761 2010-11-03 21:50:49Z n_hibma $"); |
32 33/*- 34 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. 35 * All rights reserved. 36 * 37 * This code is derived from software contributed to The NetBSD Foundation 38 * by Lennart Augustsson (lennart@augustsson.net) at 39 * Carlstedt Research & Technology. --- 78 unchanged lines hidden (view full) --- 118 119static unsigned int ucom_cons_rx_low = 0; 120static unsigned int ucom_cons_rx_high = 0; 121 122static unsigned int ucom_cons_tx_low = 0; 123static unsigned int ucom_cons_tx_high = 0; 124 125static int ucom_cons_unit = -1; | 32 33/*- 34 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. 35 * All rights reserved. 36 * 37 * This code is derived from software contributed to The NetBSD Foundation 38 * by Lennart Augustsson (lennart@augustsson.net) at 39 * Carlstedt Research & Technology. --- 78 unchanged lines hidden (view full) --- 118 119static unsigned int ucom_cons_rx_low = 0; 120static unsigned int ucom_cons_rx_high = 0; 121 122static unsigned int ucom_cons_tx_low = 0; 123static unsigned int ucom_cons_tx_high = 0; 124 125static int ucom_cons_unit = -1; |
126static int ucom_cons_subunit = 0; |
|
126static int ucom_cons_baud = 9600; 127static struct ucom_softc *ucom_cons_softc = NULL; 128 129TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit); 130SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW, 131 &ucom_cons_unit, 0, "console unit number"); | 127static int ucom_cons_baud = 9600; 128static struct ucom_softc *ucom_cons_softc = NULL; 129 130TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit); 131SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW, 132 &ucom_cons_unit, 0, "console unit number"); |
132 | 133TUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit); 134SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW, 135 &ucom_cons_subunit, 0, "console subunit number"); |
133TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud); 134SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW, 135 &ucom_cons_baud, 0, "console baud rate"); 136 137static usb_proc_callback_t ucom_cfg_start_transfers; 138static usb_proc_callback_t ucom_cfg_open; 139static usb_proc_callback_t ucom_cfg_close; 140static usb_proc_callback_t ucom_cfg_line_state; 141static usb_proc_callback_t ucom_cfg_status_change; 142static usb_proc_callback_t ucom_cfg_param; 143 | 136TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud); 137SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW, 138 &ucom_cons_baud, 0, "console baud rate"); 139 140static usb_proc_callback_t ucom_cfg_start_transfers; 141static usb_proc_callback_t ucom_cfg_open; 142static usb_proc_callback_t ucom_cfg_close; 143static usb_proc_callback_t ucom_cfg_line_state; 144static usb_proc_callback_t ucom_cfg_status_change; 145static usb_proc_callback_t ucom_cfg_param; 146 |
144static uint8_t ucom_units_alloc(uint32_t, uint32_t *); 145static void ucom_units_free(uint32_t, uint32_t); 146static int ucom_attach_tty(struct ucom_softc *, uint32_t); | 147static int ucom_unit_alloc(void); 148static void ucom_unit_free(int); 149static int ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *); |
147static void ucom_detach_tty(struct ucom_softc *); 148static void ucom_queue_command(struct ucom_softc *, 149 usb_proc_callback_t *, struct termios *pt, 150 struct usb_proc_msg *t0, struct usb_proc_msg *t1); 151static void ucom_shutdown(struct ucom_softc *); 152static void ucom_ring(struct ucom_softc *, uint8_t); 153static void ucom_break(struct ucom_softc *, uint8_t); 154static void ucom_dtr(struct ucom_softc *, uint8_t); --- 16 unchanged lines hidden (view full) --- 171 .tsw_param = ucom_param, 172 .tsw_modem = ucom_modem, 173 .tsw_free = ucom_free, 174}; 175 176MODULE_DEPEND(ucom, usb, 1, 1, 1); 177MODULE_VERSION(ucom, 1); 178 | 150static void ucom_detach_tty(struct ucom_softc *); 151static void ucom_queue_command(struct ucom_softc *, 152 usb_proc_callback_t *, struct termios *pt, 153 struct usb_proc_msg *t0, struct usb_proc_msg *t1); 154static void ucom_shutdown(struct ucom_softc *); 155static void ucom_ring(struct ucom_softc *, uint8_t); 156static void ucom_break(struct ucom_softc *, uint8_t); 157static void ucom_dtr(struct ucom_softc *, uint8_t); --- 16 unchanged lines hidden (view full) --- 174 .tsw_param = ucom_param, 175 .tsw_modem = ucom_modem, 176 .tsw_free = ucom_free, 177}; 178 179MODULE_DEPEND(ucom, usb, 1, 1, 1); 180MODULE_VERSION(ucom, 1); 181 |
179#define UCOM_UNIT_MAX 0x200 /* exclusive */ 180#define UCOM_SUB_UNIT_MAX 0x100 /* exclusive */ | 182#define UCOM_UNIT_MAX 128 /* limits size of ucom_bitmap */ |
181 182static uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8]; 183static struct mtx ucom_bitmap_mtx; 184MTX_SYSINIT(ucom_bitmap_mtx, &ucom_bitmap_mtx, "ucom bitmap", MTX_DEF); 185 | 183 184static uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8]; 185static struct mtx ucom_bitmap_mtx; 186MTX_SYSINIT(ucom_bitmap_mtx, &ucom_bitmap_mtx, "ucom bitmap", MTX_DEF); 187 |
186static uint8_t 187ucom_units_alloc(uint32_t sub_units, uint32_t *p_root_unit) | 188/* 189 * Mark a unit number (the X in cuaUX) as in use. 190 * 191 * Note that devices using a different naming scheme (see ucom_tty_name() 192 * callback) still use this unit allocation. 193 */ 194static int 195ucom_unit_alloc(void) |
188{ | 196{ |
189 uint32_t n; 190 uint32_t o; 191 uint32_t x; 192 uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units); 193 uint8_t error = 1; | 197 int unit; |
194 195 mtx_lock(&ucom_bitmap_mtx); 196 | 198 199 mtx_lock(&ucom_bitmap_mtx); 200 |
197 for (n = 0; n < max; n += sub_units) { | 201 for (unit = 0; unit < UCOM_UNIT_MAX; unit++) 202 if ((ucom_bitmap[unit / 8] & (1 << (unit % 8))) == 0) 203 break; |
198 | 204 |
199 /* check for free consecutive bits */ 200 201 for (o = 0; o < sub_units; o++) { 202 203 x = n + o; 204 205 if (ucom_bitmap[x / 8] & (1 << (x % 8))) { 206 goto skip; 207 } 208 } 209 210 /* allocate */ 211 212 for (o = 0; o < sub_units; o++) { 213 214 x = n + o; 215 216 ucom_bitmap[x / 8] |= (1 << (x % 8)); 217 } 218 219 error = 0; 220 221 break; 222 223skip: ; 224 } 225 | |
226 mtx_unlock(&ucom_bitmap_mtx); 227 | 205 mtx_unlock(&ucom_bitmap_mtx); 206 |
228 /* 229 * Always set the variable pointed to by "p_root_unit" so that 230 * the compiler does not think that it is used uninitialised: 231 */ 232 *p_root_unit = n; 233 234 return (error); | 207 if (unit == UCOM_UNIT_MAX) 208 return -1; 209 else 210 return unit; |
235} 236 | 211} 212 |
213/* 214 * Mark the unit number as not in use. 215 */ |
|
237static void | 216static void |
238ucom_units_free(uint32_t root_unit, uint32_t sub_units) | 217ucom_unit_free(int unit) |
239{ | 218{ |
240 uint32_t x; 241 | |
242 mtx_lock(&ucom_bitmap_mtx); 243 | 219 mtx_lock(&ucom_bitmap_mtx); 220 |
244 while (sub_units--) { 245 x = root_unit + sub_units; 246 ucom_bitmap[x / 8] &= ~(1 << (x % 8)); 247 } | 221 ucom_bitmap[unit / 8] &= ~(1 << (unit % 8)); |
248 249 mtx_unlock(&ucom_bitmap_mtx); 250} 251 252/* | 222 223 mtx_unlock(&ucom_bitmap_mtx); 224} 225 226/* |
253 * "N" sub_units are setup at a time. All sub-units will 254 * be given sequential unit numbers. The number of 255 * sub-units can be used to differentiate among 256 * different types of devices. | 227 * Setup a group of one or more serial ports. |
257 * 258 * The mutex pointed to by "mtx" is applied before all 259 * callbacks are called back. Also "mtx" must be applied 260 * before calling into the ucom-layer! 261 */ 262int 263ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc, | 228 * 229 * The mutex pointed to by "mtx" is applied before all 230 * callbacks are called back. Also "mtx" must be applied 231 * before calling into the ucom-layer! 232 */ 233int 234ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc, |
264 uint32_t sub_units, void *parent, | 235 uint32_t subunits, void *parent, |
265 const struct ucom_callback *callback, struct mtx *mtx) 266{ | 236 const struct ucom_callback *callback, struct mtx *mtx) 237{ |
267 uint32_t n; 268 uint32_t root_unit; | 238 uint32_t subunit; |
269 int error = 0; 270 271 if ((sc == NULL) || | 239 int error = 0; 240 241 if ((sc == NULL) || |
272 (sub_units == 0) || 273 (sub_units > UCOM_SUB_UNIT_MAX) || | 242 (subunits == 0) || |
274 (callback == NULL)) { 275 return (EINVAL); 276 } 277 | 243 (callback == NULL)) { 244 return (EINVAL); 245 } 246 |
278 /* XXX unit management does not really belong here */ 279 if (ucom_units_alloc(sub_units, &root_unit)) { | 247 ssc->sc_unit = ucom_unit_alloc(); 248 if (ssc->sc_unit == -1) |
280 return (ENOMEM); | 249 return (ENOMEM); |
281 } | |
282 283 error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED); 284 if (error) { | 250 251 error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED); 252 if (error) { |
285 ucom_units_free(root_unit, sub_units); | 253 ucom_unit_free(ssc->sc_unit); |
286 return (error); 287 } | 254 return (error); 255 } |
256 ssc->sc_subunits = subunits; |
|
288 | 257 |
289 for (n = 0; n != sub_units; n++, sc++) { 290 sc->sc_unit = root_unit + n; 291 sc->sc_local_unit = n; 292 sc->sc_super = ssc; 293 sc->sc_mtx = mtx; 294 sc->sc_parent = parent; 295 sc->sc_callback = callback; | 258 for (subunit = 0; subunit != ssc->sc_subunits; subunit++) { 259 sc[subunit].sc_subunit = subunit; 260 sc[subunit].sc_super = ssc; 261 sc[subunit].sc_mtx = mtx; 262 sc[subunit].sc_parent = parent; 263 sc[subunit].sc_callback = callback; |
296 | 264 |
297 error = ucom_attach_tty(sc, sub_units); | 265 error = ucom_attach_tty(ssc, &sc[subunit]); |
298 if (error) { | 266 if (error) { |
299 ucom_detach(ssc, sc - n, n); 300 ucom_units_free(root_unit + n, sub_units - n); | 267 ucom_detach(ssc, &sc[0]); |
301 return (error); 302 } | 268 return (error); 269 } |
303 sc->sc_flag |= UCOM_FLAG_ATTACHED; | 270 sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED; |
304 } | 271 } |
272 273 DPRINTF("tp = %p, unit = %d, subunits = %d, device name subunit 0 = %s\n", 274 sc->sc_tty, ssc->sc_unit, ssc->sc_subunits, sc[0].sc_devname); 275 |
|
305 return (0); 306} 307 308/* 309 * NOTE: the following function will do nothing if 310 * the structure pointed to by "ssc" and "sc" is zero. 311 */ 312void | 276 return (0); 277} 278 279/* 280 * NOTE: the following function will do nothing if 281 * the structure pointed to by "ssc" and "sc" is zero. 282 */ 283void |
313ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc, 314 uint32_t sub_units) | 284ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc) |
315{ | 285{ |
316 uint32_t n; | 286 uint32_t subunit; |
317 318 usb_proc_drain(&ssc->sc_tq); 319 | 287 288 usb_proc_drain(&ssc->sc_tq); 289 |
320 for (n = 0; n != sub_units; n++, sc++) { 321 if (sc->sc_flag & UCOM_FLAG_ATTACHED) { | 290 for (subunit = 0; subunit <= ssc->sc_subunits; subunit++) { 291 if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) { |
322 | 292 |
323 ucom_detach_tty(sc); | 293 ucom_detach_tty(&sc[subunit]); |
324 | 294 |
325 ucom_units_free(sc->sc_unit, 1); 326 327 /* avoid duplicate detach: */ 328 sc->sc_flag &= ~UCOM_FLAG_ATTACHED; | 295 /* avoid duplicate detach */ 296 sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED; |
329 } 330 } | 297 } 298 } |
299 ucom_unit_free(ssc->sc_unit); |
|
331 usb_proc_free(&ssc->sc_tq); 332} 333 334static int | 300 usb_proc_free(&ssc->sc_tq); 301} 302 303static int |
335ucom_attach_tty(struct ucom_softc *sc, uint32_t sub_units) | 304ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc) |
336{ 337 struct tty *tp; | 305{ 306 struct tty *tp; |
338 int error = 0; 339 char buf[32]; /* temporary TTY device name buffer */ | 307 char buf[10]; /* temporary TTY device name buffer */ |
340 341 tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx); | 308 309 tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx); |
342 if (tp == NULL) { 343 error = ENOMEM; 344 goto done; 345 } 346 DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit); | 310 if (tp == NULL) 311 return (ENOMEM); |
347 | 312 |
348 buf[0] = 0; /* set some default value */ 349 | |
350 /* Check if the client has a custom TTY name */ | 313 /* Check if the client has a custom TTY name */ |
314 buf[0] = '\0'; |
|
351 if (sc->sc_callback->ucom_tty_name) { 352 sc->sc_callback->ucom_tty_name(sc, buf, | 315 if (sc->sc_callback->ucom_tty_name) { 316 sc->sc_callback->ucom_tty_name(sc, buf, |
353 sizeof(buf), sc->sc_local_unit); | 317 sizeof(buf), ssc->sc_unit, sc->sc_subunit); |
354 } 355 if (buf[0] == 0) { 356 /* Use default TTY name */ | 318 } 319 if (buf[0] == 0) { 320 /* Use default TTY name */ |
357 if (sub_units > 1) { | 321 if (ssc->sc_subunits > 1) { |
358 /* multiple modems in one */ | 322 /* multiple modems in one */ |
359 if (snprintf(buf, sizeof(buf), "U%u.%u", 360 sc->sc_unit - sc->sc_local_unit, 361 sc->sc_local_unit)) { 362 /* ignore */ 363 } | 323 snprintf(buf, sizeof(buf), "U%u.%u", 324 ssc->sc_unit, sc->sc_subunit); |
364 } else { 365 /* single modem */ | 325 } else { 326 /* single modem */ |
366 if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) { 367 /* ignore */ 368 } | 327 snprintf(buf, sizeof(buf), "U%u", ssc->sc_unit); |
369 } 370 } 371 tty_makedev(tp, NULL, "%s", buf); 372 373 sc->sc_tty = tp; 374 375 DPRINTF("ttycreate: %s\n", buf); 376 cv_init(&sc->sc_cv, "ucom"); 377 378 /* Check if this device should be a console */ 379 if ((ucom_cons_softc == NULL) && | 328 } 329 } 330 tty_makedev(tp, NULL, "%s", buf); 331 332 sc->sc_tty = tp; 333 334 DPRINTF("ttycreate: %s\n", buf); 335 cv_init(&sc->sc_cv, "ucom"); 336 337 /* Check if this device should be a console */ 338 if ((ucom_cons_softc == NULL) && |
380 (sc->sc_unit == ucom_cons_unit)) { 381 | 339 (ssc->sc_unit == ucom_cons_unit) && 340 (sc->sc_subunit == ucom_cons_subunit)) { |
382 struct termios t; 383 | 341 struct termios t; 342 |
343 DPRINTF("unit %d subunit %d is console", ssc->sc_unit, sc->sc_subunit); 344 |
|
384 ucom_cons_softc = sc; 385 386 memset(&t, 0, sizeof(t)); 387 t.c_ispeed = ucom_cons_baud; 388 t.c_ospeed = t.c_ispeed; 389 t.c_cflag = CS8; 390 391 mtx_lock(ucom_cons_softc->sc_mtx); 392 ucom_cons_rx_low = 0; 393 ucom_cons_rx_high = 0; 394 ucom_cons_tx_low = 0; 395 ucom_cons_tx_high = 0; 396 sc->sc_flag |= UCOM_FLAG_CONSOLE; 397 ucom_open(ucom_cons_softc->sc_tty); 398 ucom_param(ucom_cons_softc->sc_tty, &t); 399 mtx_unlock(ucom_cons_softc->sc_mtx); 400 } | 345 ucom_cons_softc = sc; 346 347 memset(&t, 0, sizeof(t)); 348 t.c_ispeed = ucom_cons_baud; 349 t.c_ospeed = t.c_ispeed; 350 t.c_cflag = CS8; 351 352 mtx_lock(ucom_cons_softc->sc_mtx); 353 ucom_cons_rx_low = 0; 354 ucom_cons_rx_high = 0; 355 ucom_cons_tx_low = 0; 356 ucom_cons_tx_high = 0; 357 sc->sc_flag |= UCOM_FLAG_CONSOLE; 358 ucom_open(ucom_cons_softc->sc_tty); 359 ucom_param(ucom_cons_softc->sc_tty, &t); 360 mtx_unlock(ucom_cons_softc->sc_mtx); 361 } |
401done: 402 return (error); | 362 363 return (0); |
403} 404 405static void 406ucom_detach_tty(struct ucom_softc *sc) 407{ 408 struct tty *tp = sc->sc_tty; 409 410 DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty); 411 412 if (sc->sc_flag & UCOM_FLAG_CONSOLE) { 413 mtx_lock(ucom_cons_softc->sc_mtx); 414 ucom_close(ucom_cons_softc->sc_tty); | 364} 365 366static void 367ucom_detach_tty(struct ucom_softc *sc) 368{ 369 struct tty *tp = sc->sc_tty; 370 371 DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty); 372 373 if (sc->sc_flag & UCOM_FLAG_CONSOLE) { 374 mtx_lock(ucom_cons_softc->sc_mtx); 375 ucom_close(ucom_cons_softc->sc_tty); |
376 sc->sc_flag &= ~UCOM_FLAG_CONSOLE; |
|
415 mtx_unlock(ucom_cons_softc->sc_mtx); 416 ucom_cons_softc = NULL; 417 } 418 419 /* the config thread has been stopped when we get here */ 420 421 mtx_lock(sc->sc_mtx); 422 sc->sc_flag |= UCOM_FLAG_GONE; --- 1034 unchanged lines hidden --- | 377 mtx_unlock(ucom_cons_softc->sc_mtx); 378 ucom_cons_softc = NULL; 379 } 380 381 /* the config thread has been stopped when we get here */ 382 383 mtx_lock(sc->sc_mtx); 384 sc->sc_flag |= UCOM_FLAG_GONE; --- 1034 unchanged lines hidden --- |